Zlr3-NFJ@NFaJlhyfcw{k^vvtGl`N9xSo**rDW4S}i
zM9{fMPWo%4wYDG~BZ18BD+}h|GQKc-g^{++3MY>}W_uq7jGHx{mwE9fZiPCoxN$+7
zrODGGJrOkcPQUB(FD5aoS4g~7#6NR^ma7-!>mHuJfY5kTe6PpNNKC9GGRiu^L31uG
z$7v`*JknQHsYB!Tm_W{a32TM099djW%5e+j0Ve_ct}IM>XLF1Ap+YvcrLV=|CKo6S
zb+9Nl3_YdKP6%Cxy@6TxZ>;4&nTneadr
z_ES90ydCev)LV!dN=#(*f}|ZORFdvkYBni^aLbUk>BajeWIOcmHP#8S)*2U~QKI%S
zyrLmtPqb&TphJ;>yAxri#;{uyk`JJqODDw%(Z=2`1uc}br^V%>j!gS)D*q*f_-qf8&D;W1dJgQMlaH5er
zN2U<%Smb7==vE}dDI8K7cKz!vs^73o9f>2sgiTzWcwY|BMYHH5%Vn7#kiw&eItCqa
zIkR2~Q}>X=Ar8W|^Ms41Fm8o6IB2_j60eOeBB1Br!boW7JnoeX6Gs)?7rW0^5psc-
zjS16yb>dFn>KPOF;imD}e!enuIniFzv}n$m2#gCCv4jM#ArwlzZ$7@9&XkFxZ4n!V
zj3dyiwW4Ki2QG{@i>yuZXQizw_OkZI^-3otXC{!(lUpJF33gI60ak;Uqitp74|B6I
zgg{b=Iz}WkhCGj1M=hu4#Aw173YxIVbISaoc
z-nLZC*6Tgivd5V`K%GxhBsp@SUU60-rfc$=wb>zdJzXS&-5(NRRodFk;Kxk!S(O(a0e7oY=E(
zAyS;Ow?6Q&XA+cnkCb{28_1N8H#?J!*$MmIwLq^*T_9-z^&UE@A(z9oGYtFy6EZef
LrJugUA?W`A8`#=m
literal 0
HcmV?d00001
diff --git a/dashboard/src/app/globals.css b/dashboard/src/app/globals.css
new file mode 100644
index 0000000..c56032b
--- /dev/null
+++ b/dashboard/src/app/globals.css
@@ -0,0 +1,130 @@
+@import "tailwindcss";
+@import "tw-animate-css";
+@import "shadcn/tailwind.css";
+
+@custom-variant dark (&:is(.dark *));
+
+@theme inline {
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --font-sans: var(--font-sans);
+ --font-mono: var(--font-geist-mono);
+ --font-heading: var(--font-sans);
+ --color-sidebar-ring: var(--sidebar-ring);
+ --color-sidebar-border: var(--sidebar-border);
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
+ --color-sidebar-accent: var(--sidebar-accent);
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
+ --color-sidebar-primary: var(--sidebar-primary);
+ --color-sidebar-foreground: var(--sidebar-foreground);
+ --color-sidebar: var(--sidebar);
+ --color-chart-5: var(--chart-5);
+ --color-chart-4: var(--chart-4);
+ --color-chart-3: var(--chart-3);
+ --color-chart-2: var(--chart-2);
+ --color-chart-1: var(--chart-1);
+ --color-ring: var(--ring);
+ --color-input: var(--input);
+ --color-border: var(--border);
+ --color-destructive: var(--destructive);
+ --color-accent-foreground: var(--accent-foreground);
+ --color-accent: var(--accent);
+ --color-muted-foreground: var(--muted-foreground);
+ --color-muted: var(--muted);
+ --color-secondary-foreground: var(--secondary-foreground);
+ --color-secondary: var(--secondary);
+ --color-primary-foreground: var(--primary-foreground);
+ --color-primary: var(--primary);
+ --color-popover-foreground: var(--popover-foreground);
+ --color-popover: var(--popover);
+ --color-card-foreground: var(--card-foreground);
+ --color-card: var(--card);
+ --radius-sm: calc(var(--radius) * 0.6);
+ --radius-md: calc(var(--radius) * 0.8);
+ --radius-lg: var(--radius);
+ --radius-xl: calc(var(--radius) * 1.4);
+ --radius-2xl: calc(var(--radius) * 1.8);
+ --radius-3xl: calc(var(--radius) * 2.2);
+ --radius-4xl: calc(var(--radius) * 2.6);
+}
+
+:root {
+ --background: oklch(1 0 0);
+ --foreground: oklch(0.145 0 0);
+ --card: oklch(1 0 0);
+ --card-foreground: oklch(0.145 0 0);
+ --popover: oklch(1 0 0);
+ --popover-foreground: oklch(0.145 0 0);
+ --primary: oklch(0.205 0 0);
+ --primary-foreground: oklch(0.985 0 0);
+ --secondary: oklch(0.97 0 0);
+ --secondary-foreground: oklch(0.205 0 0);
+ --muted: oklch(0.97 0 0);
+ --muted-foreground: oklch(0.556 0 0);
+ --accent: oklch(0.97 0 0);
+ --accent-foreground: oklch(0.205 0 0);
+ --destructive: oklch(0.577 0.245 27.325);
+ --border: oklch(0.922 0 0);
+ --input: oklch(0.922 0 0);
+ --ring: oklch(0.708 0 0);
+ --chart-1: oklch(0.87 0 0);
+ --chart-2: oklch(0.556 0 0);
+ --chart-3: oklch(0.439 0 0);
+ --chart-4: oklch(0.371 0 0);
+ --chart-5: oklch(0.269 0 0);
+ --radius: 0.625rem;
+ --sidebar: oklch(0.985 0 0);
+ --sidebar-foreground: oklch(0.145 0 0);
+ --sidebar-primary: oklch(0.205 0 0);
+ --sidebar-primary-foreground: oklch(0.985 0 0);
+ --sidebar-accent: oklch(0.97 0 0);
+ --sidebar-accent-foreground: oklch(0.205 0 0);
+ --sidebar-border: oklch(0.922 0 0);
+ --sidebar-ring: oklch(0.708 0 0);
+}
+
+.dark {
+ --background: oklch(0.145 0 0);
+ --foreground: oklch(0.985 0 0);
+ --card: oklch(0.205 0 0);
+ --card-foreground: oklch(0.985 0 0);
+ --popover: oklch(0.205 0 0);
+ --popover-foreground: oklch(0.985 0 0);
+ --primary: oklch(0.922 0 0);
+ --primary-foreground: oklch(0.205 0 0);
+ --secondary: oklch(0.269 0 0);
+ --secondary-foreground: oklch(0.985 0 0);
+ --muted: oklch(0.269 0 0);
+ --muted-foreground: oklch(0.708 0 0);
+ --accent: oklch(0.269 0 0);
+ --accent-foreground: oklch(0.985 0 0);
+ --destructive: oklch(0.704 0.191 22.216);
+ --border: oklch(1 0 0 / 10%);
+ --input: oklch(1 0 0 / 15%);
+ --ring: oklch(0.556 0 0);
+ --chart-1: oklch(0.87 0 0);
+ --chart-2: oklch(0.556 0 0);
+ --chart-3: oklch(0.439 0 0);
+ --chart-4: oklch(0.371 0 0);
+ --chart-5: oklch(0.269 0 0);
+ --sidebar: oklch(0.205 0 0);
+ --sidebar-foreground: oklch(0.985 0 0);
+ --sidebar-primary: oklch(0.488 0.243 264.376);
+ --sidebar-primary-foreground: oklch(0.985 0 0);
+ --sidebar-accent: oklch(0.269 0 0);
+ --sidebar-accent-foreground: oklch(0.985 0 0);
+ --sidebar-border: oklch(1 0 0 / 10%);
+ --sidebar-ring: oklch(0.556 0 0);
+}
+
+@layer base {
+ * {
+ @apply border-border outline-ring/50;
+ }
+ body {
+ @apply bg-background text-foreground;
+ }
+ html {
+ @apply font-sans;
+ }
+}
\ No newline at end of file
diff --git a/dashboard/src/app/layout.tsx b/dashboard/src/app/layout.tsx
new file mode 100644
index 0000000..8b3023a
--- /dev/null
+++ b/dashboard/src/app/layout.tsx
@@ -0,0 +1,38 @@
+import type { Metadata } from "next";
+import { Geist, Geist_Mono } from "next/font/google";
+import { Toaster } from "@/components/ui/sonner";
+import Sidebar from "@/components/Sidebar";
+import { ProjectProvider } from "@/hooks/useProjectKey";
+import "./globals.css";
+
+const geistSans = Geist({
+ variable: "--font-geist-sans",
+ subsets: ["latin"],
+});
+
+const geistMono = Geist_Mono({
+ variable: "--font-geist-mono",
+ subsets: ["latin"],
+});
+
+export const metadata: Metadata = {
+ title: "Notifkit Observability",
+ description: "Real-time delivery observability dashboard for Notifkit",
+};
+
+export default function RootLayout({ children }: { children: React.ReactNode }) {
+ return (
+
+
+
+
+ {children}
+
+
+
+
+ );
+}
diff --git a/dashboard/src/app/logs/page.tsx b/dashboard/src/app/logs/page.tsx
new file mode 100644
index 0000000..52953e1
--- /dev/null
+++ b/dashboard/src/app/logs/page.tsx
@@ -0,0 +1,20 @@
+"use client";
+
+import DashboardHeader from "@/components/DashboardHeader";
+import LogsTable from "@/components/LogsTable";
+
+export default function LogsPage() {
+ return (
+
+ );
+}
diff --git a/dashboard/src/app/page.tsx b/dashboard/src/app/page.tsx
new file mode 100644
index 0000000..189b6b8
--- /dev/null
+++ b/dashboard/src/app/page.tsx
@@ -0,0 +1,23 @@
+"use client";
+
+import { useState } from "react";
+import DashboardHeader from "@/components/DashboardHeader";
+import RealTimeFeed from "@/components/RealTimeFeed";
+
+export default function Home() {
+ const [isConnected, setIsConnected] = useState(false);
+
+ return (
+
+ );
+}
diff --git a/dashboard/src/app/projects/page.tsx b/dashboard/src/app/projects/page.tsx
new file mode 100644
index 0000000..f0b7d45
--- /dev/null
+++ b/dashboard/src/app/projects/page.tsx
@@ -0,0 +1,375 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import DashboardHeader from "@/components/DashboardHeader";
+import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
+import { Button } from "@/components/ui/button";
+import { toast } from "sonner";
+import { FolderKey, Plus, Copy, Trash2, AlertCircle, Eye, EyeOff } from "lucide-react";
+
+interface Project {
+ id: string;
+ name: string;
+ rateLimitRpm: number | null;
+ createdAt: string;
+}
+
+interface ApiKey {
+ id: string;
+ role: string;
+ createdAt: string;
+}
+
+export default function ProjectsPage() {
+ const [projects, setProjects] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [newProjectName, setNewProjectName] = useState("");
+ const [selectedProjectId, setSelectedProjectId] = useState(null);
+ const [apiKeys, setApiKeys] = useState([]);
+
+ const [newlyGeneratedKey, setNewlyGeneratedKey] = useState(null);
+ const [showKey, setShowKey] = useState(false);
+
+ const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000";
+ const adminKey = process.env.NEXT_PUBLIC_ADMIN_KEY || "nk_live_1234567890abcdef1234567890abcdef";
+
+ const fetchProjects = async () => {
+ try {
+ const res = await fetch(`${apiUrl}/v1/projects`, {
+ headers: { Authorization: `Bearer ${adminKey}` },
+ });
+ if (res.ok) {
+ const data = await res.json();
+ setProjects(data.projects || []);
+ }
+ } catch (err) {
+ console.error(err);
+ toast.error("Failed to load projects");
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const fetchApiKeys = async (projectId: string) => {
+ try {
+ const res = await fetch(`${apiUrl}/v1/projects/${projectId}/keys`, {
+ headers: { Authorization: `Bearer ${adminKey}` },
+ });
+ if (res.ok) {
+ const data = await res.json();
+ setApiKeys(data.keys || []);
+ }
+ } catch (err) {
+ console.error(err);
+ toast.error("Failed to load API keys");
+ }
+ };
+
+ useEffect(() => {
+ void fetchProjects();
+ }, []);
+
+ useEffect(() => {
+ if (selectedProjectId) {
+ void fetchApiKeys(selectedProjectId);
+ setNewlyGeneratedKey(null);
+ setShowKey(false);
+ } else {
+ setApiKeys([]);
+ }
+ }, [selectedProjectId]);
+
+ const handleCreateProject = async (e: React.FormEvent) => {
+ e.preventDefault();
+ if (!newProjectName.trim()) return;
+
+ try {
+ const res = await fetch(`${apiUrl}/v1/projects`, {
+ method: "POST",
+ headers: {
+ Authorization: `Bearer ${adminKey}`,
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({ name: newProjectName }),
+ });
+
+ if (res.ok) {
+ const data = await res.json();
+ toast.success("Project created!");
+ setNewProjectName("");
+ void fetchProjects();
+ if (data.apiKey) {
+ setNewlyGeneratedKey(data.apiKey);
+ setShowKey(false);
+ }
+ } else {
+ const err = await res.json();
+ toast.error(`Error: ${err.message || "Failed to create project"}`);
+ }
+ } catch (_err) {
+ toast.error("Failed to create project");
+ }
+ };
+
+ const handleGenerateKey = async () => {
+ if (!selectedProjectId) return;
+ try {
+ const res = await fetch(`${apiUrl}/v1/projects/${selectedProjectId}/keys`, {
+ method: "POST",
+ headers: {
+ Authorization: `Bearer ${adminKey}`,
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({ role: "admin" }),
+ });
+ if (res.ok) {
+ const data = await res.json();
+ setNewlyGeneratedKey(data.apiKey);
+ setShowKey(false);
+ toast.success("New API Key generated successfully");
+ void fetchApiKeys(selectedProjectId);
+ } else {
+ toast.error("Failed to generate key");
+ }
+ } catch (_err) {
+ toast.error("Failed to generate key");
+ }
+ };
+
+ const handleRevokeKey = async (keyId: string) => {
+ if (!selectedProjectId) return;
+ if (
+ !confirm(
+ "Are you sure you want to revoke this API key? Systems using it will immediately stop working.",
+ )
+ )
+ return;
+ try {
+ const res = await fetch(`${apiUrl}/v1/projects/${selectedProjectId}/keys/${keyId}`, {
+ method: "DELETE",
+ headers: { Authorization: `Bearer ${adminKey}` },
+ });
+ if (res.ok) {
+ toast.success("API Key revoked");
+ void fetchApiKeys(selectedProjectId);
+ } else {
+ toast.error("Failed to revoke key");
+ }
+ } catch (_err) {
+ toast.error("Failed to revoke key");
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
+ Projects & API Keys
+
+
+
+
+
+
+ Create Project
+ Add a new tenant to Notifkit
+
+
+
+
+
+
+
+
+ Existing Projects
+
+
+ {loading ? (
+ Loading projects...
+ ) : projects.length === 0 ? (
+
+ No projects found. Create one to get started.
+
+ ) : (
+
+
+ Name
+ Rate Limit
+ Created
+
+ {projects.map((p) => (
+ setSelectedProjectId(p.id)}
+ >
+
+ {p.name}
+ {selectedProjectId === p.id && (
+
+ )}
+
+
+
+
+ {p.rateLimitRpm ? `${p.rateLimitRpm} RPM Quota` : "600 RPM (Default)"}
+
+
+ Sliding Window Active
+
+
+
+
+
+ {new Date(p.createdAt).toLocaleDateString()}
+
+
+ ))}
+
+ )}
+
+ {selectedProjectId && (
+
+
+ API Keys
+
+
+
+ {newlyGeneratedKey && (
+
+
+
+
+
+ Please copy your new API key
+
+
+ For security reasons, this key will not be shown again. Make sure to
+ copy it now.
+
+
+
+
+
+ {showKey
+ ? newlyGeneratedKey
+ : newlyGeneratedKey.replace(
+ /(?<=^nk_live_).*/,
+ "********************************",
+ )}
+
+
+
+
+
+ )}
+
+ {apiKeys.length === 0 ? (
+
+ No API keys found for this project.
+
+ ) : (
+
+
+ Key ID
+ Role
+ Actions
+
+ {apiKeys.map((k) => (
+
+
+ {k.id}
+
+ {k.role}
+
+
+
+
+ ))}
+
+ )}
+
+ )}
+
+
+
+
+
+ );
+}
diff --git a/dashboard/src/app/scheduled/page.tsx b/dashboard/src/app/scheduled/page.tsx
new file mode 100644
index 0000000..7f72ea2
--- /dev/null
+++ b/dashboard/src/app/scheduled/page.tsx
@@ -0,0 +1,185 @@
+"use client";
+
+import { useEffect, useState, useCallback } from "react";
+import DashboardHeader from "@/components/DashboardHeader";
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table";
+import { Badge } from "@/components/ui/badge";
+import { Button } from "@/components/ui/button";
+import { Clock, RefreshCcw, XCircle } from "lucide-react";
+import { toast } from "sonner";
+import { useProject } from "@/hooks/useProjectKey";
+
+interface ScheduledItem {
+ taskId: string;
+ payload: any;
+ createdAt: string;
+}
+
+export default function ScheduledMessagesPage() {
+ const [items, setItems] = useState([]);
+ const [isLoading, setIsLoading] = useState(true);
+ const { projectApiKey, selectedProjectId } = useProject();
+
+ const fetchScheduled = useCallback(async () => {
+ setIsLoading(true);
+ try {
+ const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000";
+ const res = await fetch(`${apiUrl}/v1/notifications/scheduled`, {
+ headers: {
+ Authorization: `Bearer ${projectApiKey}`,
+ "x-project-id": selectedProjectId,
+ },
+ });
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
+ const data = await res.json();
+ setItems(data.scheduled || []);
+ } catch (err: any) {
+ console.error(err);
+ toast.error("Failed to fetch scheduled notifications");
+ } finally {
+ setIsLoading(false);
+ }
+ }, [projectApiKey, selectedProjectId]);
+
+ useEffect(() => {
+ if (projectApiKey && selectedProjectId) {
+ void fetchScheduled();
+ }
+ }, [projectApiKey, selectedProjectId, fetchScheduled]);
+
+ const handleCancel = async (taskId: string) => {
+ try {
+ const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000";
+ const res = await fetch(`${apiUrl}/v1/notifications/${taskId}`, {
+ method: "DELETE",
+ headers: {
+ Authorization: `Bearer ${projectApiKey}`,
+ "x-project-id": selectedProjectId,
+ },
+ });
+ if (!res.ok) throw new Error("Cancel failed");
+ toast.success(`Task ${taskId} canceled`);
+ void fetchScheduled();
+ } catch (_err: any) {
+ toast.error("Failed to cancel scheduled task");
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ Scheduled & Deferred Notification Pipeline
+
+
+ Notifications queued for future timestamps or deferred due to recipient quiet hours
+
+
+
+
+
+
+
+ Pending Scheduled Messages ({items.length})
+
+ Managed via Redis Sorted Sets (ZSET) and persistent storage
+
+
+
+
+
+
+ Created At
+ Task ID
+ Template
+ Target
+ Scheduled Send At
+ Actions
+
+
+
+ {items.length === 0 ? (
+
+
+ No pending scheduled or deferred messages found.
+
+
+ ) : (
+ items.map((item) => {
+ const sendAt = item.payload?.scheduledAt;
+ const recipientId =
+ item.payload?.recipientId ||
+ item.payload?.recipient?.id ||
+ (typeof item.payload?.target === "object"
+ ? JSON.stringify(item.payload?.target)
+ : String(item.payload?.target || "N/A"));
+ return (
+
+
+ {new Date(item.createdAt).toLocaleString()}
+
+
+ {item.taskId}
+
+
+ {item.payload?.templateId || item.payload?.template || "N/A"}
+
+
+ {recipientId}
+
+
+
+ {sendAt ? new Date(sendAt).toLocaleString() : "Pending Dispatch"}
+
+
+
+
+
+
+ );
+ })
+ )}
+
+
+
+
+
+
+ );
+}
diff --git a/dashboard/src/app/system/page.tsx b/dashboard/src/app/system/page.tsx
new file mode 100644
index 0000000..6b4048a
--- /dev/null
+++ b/dashboard/src/app/system/page.tsx
@@ -0,0 +1,207 @@
+"use client";
+
+import { useEffect, useState, useCallback } from "react";
+import DashboardHeader from "@/components/DashboardHeader";
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
+import { Badge } from "@/components/ui/badge";
+import { Button } from "@/components/ui/button";
+import {
+ HeartPulse,
+ Database,
+ Server,
+ Cpu,
+ RefreshCcw,
+ CheckCircle2,
+ AlertCircle,
+ XCircle,
+} from "lucide-react";
+import { toast } from "sonner";
+import { useProject } from "@/hooks/useProjectKey";
+
+interface WorkerHealth {
+ state?: string;
+ status?: string;
+ redis?: boolean;
+ activeTasks?: number;
+ lastHeartbeat?: number;
+ error?: string;
+}
+
+interface SystemHealthData {
+ status: "healthy" | "degraded" | "down";
+ redis: { ok: boolean; latencyMs: number };
+ db: { ok: boolean; latencyMs: number };
+ workers: Record;
+}
+
+export default function SystemHealthPage() {
+ const [data, setData] = useState(null);
+ const [isLoading, setIsLoading] = useState(true);
+ const { projectApiKey, selectedProjectId } = useProject();
+
+ const fetchHealth = useCallback(async () => {
+ setIsLoading(true);
+ try {
+ const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000";
+ const res = await fetch(`${apiUrl}/v1/system/health`, {
+ headers: {
+ Authorization: `Bearer ${projectApiKey}`,
+ "x-project-id": selectedProjectId,
+ },
+ });
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
+ const healthData = await res.json();
+ setData(healthData);
+ } catch (err: any) {
+ console.error(err);
+ toast.error("Failed to fetch system health");
+ } finally {
+ setIsLoading(false);
+ }
+ }, [projectApiKey, selectedProjectId]);
+
+ useEffect(() => {
+ if (projectApiKey && selectedProjectId) {
+ void fetchHealth();
+ const interval = setInterval(() => void fetchHealth(), 5000);
+ return () => clearInterval(interval);
+ }
+ }, [projectApiKey, selectedProjectId, fetchHealth]);
+
+ const workerNames = ["enricher", "engine", "scheduler", "delivery", "ai", "workflow", "events"];
+
+ return (
+
+
+
+
+
+
+
+
+ System Infrastructure & Worker Health
+
+
+ Real-time heartbeats, database/Redis latencies, and microservice status
+
+
+
+
+
+ {/* Top KPI Cards: Redis & DB latency */}
+
+
+
+
+ Redis Connection
+
+
+
+
+
+ {data?.redis?.latencyMs ?? 0} ms
+
+ {data?.redis?.ok ? "Connected" : "Disconnected"}
+
+
+
+
+
+
+
+
+ PostgreSQL Database
+
+
+
+
+
+ {data?.db?.latencyMs ?? 0} ms
+
+ {data?.db?.ok ? "Healthy" : "Unreachable"}
+
+
+
+
+
+
+
+
+ Overall Cluster State
+
+
+
+
+
+ {data?.status ?? "Unknown"}
+
+
+
+
+
+
+ {/* Worker Microservices Grid */}
+
+
+ Worker Heartbeats & Concurrency
+ Monitored active workers in the event pipeline
+
+
+
+ {workerNames.map((wName) => {
+ const wInfo = data?.workers?.[wName];
+ const isAlive = wInfo && wInfo.status !== "error" && wInfo.status !== "unknown";
+
+ return (
+
+
+ {wName} Worker
+
+
+
+
+
+ Status:
+
+ {wInfo?.state || wInfo?.status || "Idle / Standing By"}
+
+
+
+ Active Concurrency:
+
+ {wInfo?.activeTasks ?? 0} tasks
+
+
+
+
+ );
+ })}
+
+
+
+
+
+ );
+}
+
+function StatusIcon({ status }: { status?: string }) {
+ if (status === "healthy") return ;
+ if (status === "degraded") return ;
+ return ;
+}
diff --git a/dashboard/src/app/templates/page.tsx b/dashboard/src/app/templates/page.tsx
new file mode 100644
index 0000000..6927a47
--- /dev/null
+++ b/dashboard/src/app/templates/page.tsx
@@ -0,0 +1,223 @@
+"use client";
+
+import { useEffect, useState, useCallback } from "react";
+import DashboardHeader from "@/components/DashboardHeader";
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table";
+import { Badge } from "@/components/ui/badge";
+import { Button } from "@/components/ui/button";
+import { FileCode2, RefreshCcw, Eye, Trash2 } from "lucide-react";
+import { toast } from "sonner";
+import { useProject } from "@/hooks/useProjectKey";
+
+interface Template {
+ id: string;
+ projectId: string;
+ channel: string;
+ topics: string[];
+ content: any;
+ aiPrompts?: any;
+ updatedAt: string;
+}
+
+export default function TemplatesPage() {
+ const [templates, setTemplates] = useState([]);
+ const [isLoading, setIsLoading] = useState(true);
+ const [selectedTemplate, setSelectedTemplate] = useState(null);
+ const { projectApiKey, selectedProjectId } = useProject();
+
+ const fetchTemplates = useCallback(async () => {
+ setIsLoading(true);
+ try {
+ const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000";
+ const res = await fetch(`${apiUrl}/v1/templates`, {
+ headers: {
+ Authorization: `Bearer ${projectApiKey}`,
+ "x-project-id": selectedProjectId,
+ },
+ });
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
+ const data = await res.json();
+ setTemplates(data.templates || []);
+ } catch (err: any) {
+ console.error(err);
+ toast.error("Failed to fetch templates");
+ } finally {
+ setIsLoading(false);
+ }
+ }, [projectApiKey, selectedProjectId]);
+
+ useEffect(() => {
+ if (projectApiKey && selectedProjectId) {
+ void fetchTemplates();
+ }
+ }, [projectApiKey, selectedProjectId, fetchTemplates]);
+
+ const handleDelete = async (id: string) => {
+ try {
+ const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000";
+ const res = await fetch(`${apiUrl}/v1/templates/${id}`, {
+ method: "DELETE",
+ headers: {
+ Authorization: `Bearer ${projectApiKey}`,
+ "x-project-id": selectedProjectId,
+ },
+ });
+ if (!res.ok) throw new Error("Delete failed");
+ toast.success(`Template ${id} deleted`);
+ void fetchTemplates();
+ } catch (_err: any) {
+ toast.error("Failed to delete template");
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ Template Directory & Channel Bindings
+
+
+ Registered notification templates, topic bindings, and AI prompts
+
+
+
+
+
+
+
+ Notification Templates ({templates.length})
+ Managed via PUT /v1/templates API endpoint
+
+
+
+
+
+ Template ID
+ Channel
+ Topics
+ Last Updated
+ Actions
+
+
+
+ {templates.length === 0 ? (
+
+
+ No templates synced for this project. Sync using initializeApp() or PUT
+ /v1/templates
+
+
+ ) : (
+ templates.map((tpl) => (
+
+
+ {tpl.id}
+
+
+
+ {tpl.channel}
+
+
+
+
+ {(tpl.topics || []).map((t) => (
+
+ {t}
+
+ ))}
+
+
+
+ {new Date(tpl.updatedAt).toLocaleString()}
+
+
+
+
+
+
+ ))
+ )}
+
+
+
+
+
+
+ {/* Template Preview Drawer */}
+ {selectedTemplate && (
+
+
+
+ Template: {selectedTemplate.id}
+
+
+
+
+ Channel:{" "}
+
+ {selectedTemplate.channel}
+
+
+
+ Topics:{" "}
+
+ {(selectedTemplate.topics || []).join(", ")}
+
+
+
+
+
+
+ {JSON.stringify(selectedTemplate.content, null, 2)}
+
+
+
+
+ )}
+
+ );
+}
diff --git a/dashboard/src/app/users/page.tsx b/dashboard/src/app/users/page.tsx
new file mode 100644
index 0000000..24c919a
--- /dev/null
+++ b/dashboard/src/app/users/page.tsx
@@ -0,0 +1,281 @@
+"use client";
+
+import { useEffect, useState, useCallback } from "react";
+import DashboardHeader from "@/components/DashboardHeader";
+import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
+import { toast } from "sonner";
+import { Users, Clock, Mail, History, Eye } from "lucide-react";
+import { Badge } from "@/components/ui/badge";
+import { Button } from "@/components/ui/button";
+import { useProject } from "@/hooks/useProjectKey";
+
+interface User {
+ id?: string;
+ userId?: string;
+ externalId?: string;
+ email?: string;
+ createdAt: string | number;
+ attributes?: any;
+ preferences?: any;
+}
+
+interface UserDetail extends User {
+ contacts?: Array<{ id: string; channel: string; target: string; enabled: boolean }>;
+ logs?: Array<{
+ id: string;
+ taskId: string;
+ templateId: string;
+ channel: string;
+ status: string;
+ timestamp: string;
+ }>;
+}
+
+export default function UsersPage() {
+ const [users, setUsers] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [selectedUser, setSelectedUser] = useState(null);
+ const [_loadingDetail, setLoadingDetail] = useState(false);
+ const { projects, selectedProjectId, projectApiKey, isLoadingProjects } = useProject();
+
+ const fetchUsers = useCallback(async (apiKey: string, projectId: string) => {
+ setLoading(true);
+ try {
+ const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000";
+ const res = await fetch(`${apiUrl}/v1/users`, {
+ headers: {
+ Authorization: `Bearer ${apiKey}`,
+ "x-project-id": projectId,
+ },
+ });
+ if (res.ok) {
+ const data = await res.json();
+ setUsers(data.users || []);
+ } else {
+ toast.error("Failed to load users");
+ }
+ } catch (err) {
+ console.error(err);
+ toast.error("Failed to load users");
+ } finally {
+ setLoading(false);
+ }
+ }, []);
+
+ useEffect(() => {
+ if (projectApiKey && selectedProjectId) {
+ void fetchUsers(projectApiKey, selectedProjectId);
+ } else {
+ setUsers([]);
+ }
+ }, [projectApiKey, selectedProjectId, fetchUsers]);
+
+ const handleOpenDetail = async (u: User) => {
+ const id = u.externalId || u.userId || u.id;
+ if (!id) return;
+ setLoadingDetail(true);
+ try {
+ const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000";
+ const res = await fetch(`${apiUrl}/v1/users/${encodeURIComponent(id)}/details`, {
+ headers: {
+ Authorization: `Bearer ${projectApiKey}`,
+ "x-project-id": selectedProjectId,
+ },
+ });
+ if (res.ok) {
+ const data = await res.json();
+ setSelectedUser(data);
+ } else {
+ setSelectedUser({ ...u, contacts: [], logs: [] });
+ }
+ } catch {
+ setSelectedUser({ ...u, contacts: [], logs: [] });
+ } finally {
+ setLoadingDetail(false);
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
+ User Directory & Contact Preferences
+
+
+
+
+
+ Registered End-Users
+
+ Recipient profiles, channel addresses, and preference overrides
+
+
+
+ {isLoadingProjects ? (
+
+
+ Loading projects...
+
+ ) : projects.length === 0 ? (
+
+
+ No projects exist. Create one in the Projects & Keys tab.
+
+ ) : !projectApiKey ? (
+
+
+ No API key found for this project.
+
+ ) : loading ? (
+ Loading users...
+ ) : users.length === 0 ? (
+ No users found.
+ ) : (
+
+
+
+
+ | External User ID |
+ Primary Contact |
+ Created At |
+ Actions |
+
+
+
+ {users.map((user, index) => {
+ const displayId =
+ user.externalId || user.userId || user.id || `user-${index}`;
+ return (
+ void handleOpenDetail(user)}
+ >
+ |
+ {displayId}
+ |
+
+ {user.email ? (
+ user.email
+ ) : (
+ Not provided
+ )}
+ |
+
+ {new Date(user.createdAt).toLocaleDateString()}
+ |
+
+
+ |
+
+ );
+ })}
+
+
+
+ )}
+
+
+
+
+ {/* User Profile Deep-Dive Modal */}
+ {selectedUser && (
+
+
+
+
+
+ User: {selectedUser.externalId || selectedUser.userId || selectedUser.id}
+
+
+ Registered contact endpoints and preferences
+
+
+
+
+
+ {/* Registered Channels */}
+
+
+
+ Contact Channels ({selectedUser.contacts?.length ?? 0})
+
+ {!selectedUser.contacts || selectedUser.contacts.length === 0 ? (
+
+ No contact endpoints registered for this user.
+
+ ) : (
+
+ {selectedUser.contacts.map((c) => (
+
+
+
+ {c.channel}
+
+ {c.target}
+
+
+ {c.enabled !== false ? "Active" : "Opted-out"}
+
+
+ ))}
+
+ )}
+
+
+ {/* Recent Notifications for User */}
+
+
+
+ Per-User Delivery Audit Trail
+
+ {!selectedUser.logs || selectedUser.logs.length === 0 ? (
+
+ No recent notifications logged for this user.
+
+ ) : (
+
+ {selectedUser.logs.map((log) => (
+
+
+
+ {log.taskId}
+
+ ({log.templateId})
+
+
+ {log.status}
+
+
+ ))}
+
+ )}
+
+
+
+ )}
+
+ );
+}
diff --git a/dashboard/src/app/workflows/page.tsx b/dashboard/src/app/workflows/page.tsx
new file mode 100644
index 0000000..ebca1cd
--- /dev/null
+++ b/dashboard/src/app/workflows/page.tsx
@@ -0,0 +1,328 @@
+"use client";
+
+import { useEffect, useState, useCallback } from "react";
+import DashboardHeader from "@/components/DashboardHeader";
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table";
+import { Badge } from "@/components/ui/badge";
+import { Button } from "@/components/ui/button";
+import { GitFork, RefreshCcw, Play, Search, Eye, Layers } from "lucide-react";
+import { toast } from "sonner";
+import { useProject } from "@/hooks/useProjectKey";
+
+interface WorkflowDef {
+ id: string;
+ name: string;
+ steps: any[];
+ createdAt: string;
+}
+
+interface WorkflowInstance {
+ id: string;
+ projectId: string;
+ workflowName: string;
+ status: "pending" | "running" | "completed" | "failed";
+ currentStepIndex: number;
+ input: any;
+ output: any;
+ createdAt: string;
+ updatedAt: string;
+}
+
+export default function WorkflowsPage() {
+ const [workflows, setWorkflows] = useState([]);
+ const [isLoading, setIsLoading] = useState(true);
+ const [selectedWorkflow, setSelectedWorkflow] = useState(null);
+
+ const [instanceSearchId, setInstanceSearchId] = useState("");
+ const [searchedInstance, setSearchedInstance] = useState(null);
+ const [searchingInstance, setSearchingInstance] = useState(false);
+
+ const { projectApiKey, selectedProjectId } = useProject();
+
+ const fetchWorkflows = useCallback(async () => {
+ setIsLoading(true);
+ try {
+ const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000";
+ const res = await fetch(`${apiUrl}/v1/workflows`, {
+ headers: {
+ Authorization: `Bearer ${projectApiKey}`,
+ "x-project-id": selectedProjectId,
+ },
+ });
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
+ const data = await res.json();
+ setWorkflows(data.workflows || []);
+ } catch (err: any) {
+ console.error(err);
+ toast.error("Failed to fetch workflows");
+ } finally {
+ setIsLoading(false);
+ }
+ }, [projectApiKey, selectedProjectId]);
+
+ useEffect(() => {
+ if (projectApiKey && selectedProjectId) {
+ void fetchWorkflows();
+ }
+ }, [projectApiKey, selectedProjectId, fetchWorkflows]);
+
+ const handleTrigger = async (name: string) => {
+ try {
+ const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000";
+ const res = await fetch(`${apiUrl}/v1/workflows/trigger`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${projectApiKey}`,
+ "x-project-id": selectedProjectId,
+ },
+ body: JSON.stringify({ name, input: {} }),
+ });
+ if (!res.ok) throw new Error("Trigger failed");
+ const data = await res.json();
+ toast.success(`Workflow "${name}" triggered! Instance ID: ${data.instanceId}`);
+ if (data.instanceId) {
+ setInstanceSearchId(data.instanceId);
+ void handleSearchInstance(data.instanceId);
+ }
+ } catch (_err: any) {
+ toast.error("Failed to trigger workflow");
+ }
+ };
+
+ const handleSearchInstance = async (idToSearch?: string) => {
+ const targetId = idToSearch || instanceSearchId.trim();
+ if (!targetId) return;
+ setSearchingInstance(true);
+ try {
+ const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000";
+ const res = await fetch(`${apiUrl}/v1/workflows/instances/${encodeURIComponent(targetId)}`, {
+ headers: {
+ Authorization: `Bearer ${projectApiKey}`,
+ "x-project-id": selectedProjectId,
+ },
+ });
+ if (!res.ok) throw new Error("Instance not found");
+ const data = await res.json();
+ setSearchedInstance(data);
+ } catch {
+ toast.error("Workflow instance not found");
+ setSearchedInstance(null);
+ } finally {
+ setSearchingInstance(false);
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ Workflows & Event Orchestration
+
+
+ Multi-step notification orchestration definitions, step execution history, and
+ instance tracing
+
+
+
+
+
+ {/* Workflow Instance Tracing Search */}
+
+
+
+
+ Workflow Instance Lookup & Step Execution Visualizer
+
+
+ Search by instance UUID to view step-by-step execution state and payload progress
+
+
+
+
+ setInstanceSearchId(e.target.value)}
+ className="flex-1 px-3 py-1.5 bg-muted/30 border border-border/40 rounded-lg text-xs font-mono focus:outline-none focus:ring-1 focus:ring-indigo-500"
+ />
+
+
+
+ {searchedInstance && (
+
+
+
+ Instance ID:{" "}
+
+ {searchedInstance.id}
+
+
+
+ {searchedInstance.status}
+
+
+
+ Workflow Name:{" "}
+ {searchedInstance.workflowName} |
+ Step Pointer:{" "}
+
+ {searchedInstance.currentStepIndex ?? 0}
+
+
+
+ )}
+
+
+
+ {/* Registered Workflows Grid / Table */}
+
+
+ Registered Workflows ({workflows.length})
+ Definitions registered for background execution
+
+
+
+
+
+ Created At
+ Workflow Name
+ Steps Count
+ Definition Structure
+ Actions
+
+
+
+ {workflows.length === 0 ? (
+
+
+ No registered workflows found. Register workflows using POST /v1/workflows
+
+
+ ) : (
+ workflows.map((wf) => (
+
+
+ {new Date(wf.createdAt).toLocaleString()}
+
+
+ {wf.name}
+
+
+
+ {Array.isArray(wf.steps) ? wf.steps.length : 0} steps
+
+
+
+ {JSON.stringify(wf.steps)}
+
+
+
+
+
+
+ ))
+ )}
+
+
+
+
+
+
+ {/* Visual Step Execution Graph Modal */}
+ {selectedWorkflow && (
+
+
+
+
+ Workflow Diagram: {selectedWorkflow.name}
+ ID: {selectedWorkflow.id}
+
+
+
+
+
+
+
+ Execution Step Graph Flow
+
+
+
+ {(selectedWorkflow.steps || []).map((step: any, idx: number) => (
+
+
+
+ Step {idx + 1}: {step.type || step.name || "Action"}
+
+
+ {step.channel || step.action || "Step Node"}
+
+
+
+ {JSON.stringify(step, null, 2)}
+
+
+ ))}
+
+
+
+
+ )}
+
+ );
+}
diff --git a/dashboard/src/components/DashboardHeader.tsx b/dashboard/src/components/DashboardHeader.tsx
new file mode 100644
index 0000000..76bf1df
--- /dev/null
+++ b/dashboard/src/components/DashboardHeader.tsx
@@ -0,0 +1,95 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import { Activity, Bell, FolderKey, AlertTriangle } from "lucide-react";
+import { Badge } from "@/components/ui/badge";
+import { useProject } from "@/hooks/useProjectKey";
+
+export default function DashboardHeader({ isConnected }: { isConnected: boolean }) {
+ const { projects, selectedProjectId, setSelectedProjectId, projectApiKey } = useProject();
+ const [highBackpressure, setHighBackpressure] = useState(false);
+
+ useEffect(() => {
+ if (!projectApiKey || !selectedProjectId) return;
+ const checkMetrics = async () => {
+ try {
+ const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000";
+ const res = await fetch(`${apiUrl}/v1/system/metrics`, {
+ headers: {
+ Authorization: `Bearer ${projectApiKey}`,
+ "x-project-id": selectedProjectId,
+ },
+ });
+ if (res.ok) {
+ const data = await res.json();
+ const streams = data.streams || {};
+ const maxDepth = Math.max(...(Object.values(streams) as number[]), 0);
+ setHighBackpressure(maxDepth > 8000);
+ }
+ } catch {
+ // Ignore background header check errors
+ }
+ };
+
+ void checkMetrics();
+ const interval = setInterval(() => void checkMetrics(), 10000);
+ return () => clearInterval(interval);
+ }, [projectApiKey, selectedProjectId]);
+
+ return (
+
+
+
+
+
+
+ Notifkit Observability
+
+
+
+ {highBackpressure && (
+
+
+ Queue Backpressure Warning (>80% Cap)
+
+ )}
+
+ {projects.length > 0 && (
+
+
+
+
+ )}
+
+
+ {isConnected ? (
+
+
+
+
+ ) : (
+
+ )}
+ {isConnected ? "Live Stream Active" : "Disconnected"}
+
+
+
+
+ );
+}
diff --git a/dashboard/src/components/LogsTable.tsx b/dashboard/src/components/LogsTable.tsx
new file mode 100644
index 0000000..2c6d069
--- /dev/null
+++ b/dashboard/src/components/LogsTable.tsx
@@ -0,0 +1,481 @@
+"use client";
+
+import { useEffect, useState, useCallback } from "react";
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table";
+import { Badge } from "@/components/ui/badge";
+import { Button } from "@/components/ui/button";
+import {
+ RefreshCcw,
+ Database,
+ Search,
+ ChevronLeft,
+ ChevronRight,
+ Eye,
+ CheckCircle2,
+ AlertCircle,
+ Send,
+ GitCommit,
+} from "lucide-react";
+import { toast } from "sonner";
+import { useProject } from "@/hooks/useProjectKey";
+
+interface LogEntry {
+ id: string;
+ projectId: string;
+ taskId: string;
+ providerMessageId?: string;
+ templateId: string;
+ workflowInstanceId: string | null;
+ channel: string;
+ attempt?: number;
+ kind?: string;
+ status: "pending" | "dispatched" | "delivered" | "failed" | "canceled" | "skipped";
+ timestamp: string;
+}
+
+export default function LogsTable() {
+ const [logs, setLogs] = useState([]);
+ const [nextCursor, setNextCursor] = useState(null);
+ const [cursorHistory, setCursorHistory] = useState<(string | null)[]>([null]);
+ const [pageIndex, setPageIndex] = useState(0);
+ const [isLoading, setIsLoading] = useState(false);
+ const [search, setSearch] = useState("");
+ const [channelFilter, setChannelFilter] = useState("");
+ const [statusFilter, setStatusFilter] = useState("");
+ const [selectedLog, setSelectedLog] = useState(null);
+ const [taskLifecycle, setTaskLifecycle] = useState([]);
+ const [loadingLifecycle, setLoadingLifecycle] = useState(false);
+
+ const { projects, selectedProjectId, projectApiKey, isLoadingProjects } = useProject();
+
+ const fetchLogs = useCallback(
+ async (apiKey: string, projectId: string, cursor?: string | null) => {
+ setIsLoading(true);
+ try {
+ const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000";
+ const query = new URLSearchParams({ limit: "25" });
+ if (cursor) query.set("cursor", cursor);
+ if (search) query.set("search", search);
+ if (channelFilter) query.set("channel", channelFilter);
+ if (statusFilter) query.set("status", statusFilter);
+
+ const res = await fetch(`${apiUrl}/v1/notifications/logs?${query.toString()}`, {
+ headers: {
+ Authorization: `Bearer ${apiKey}`,
+ "x-project-id": projectId,
+ },
+ });
+
+ if (!res.ok) {
+ throw new Error(`Failed to fetch: ${res.statusText}`);
+ }
+
+ const data = await res.json();
+ setLogs(data.logs || []);
+ setNextCursor(data.nextCursor || null);
+ } catch (err) {
+ console.error(err);
+ toast.error("Failed to fetch logs");
+ } finally {
+ setIsLoading(false);
+ }
+ },
+ [search, channelFilter, statusFilter],
+ );
+
+ useEffect(() => {
+ if (projectApiKey && selectedProjectId) {
+ setCursorHistory([null]);
+ setPageIndex(0);
+ void fetchLogs(projectApiKey, selectedProjectId, null);
+ } else {
+ setLogs([]);
+ }
+ }, [projectApiKey, selectedProjectId, search, channelFilter, statusFilter, fetchLogs]);
+
+ useEffect(() => {
+ if (selectedLog && projectApiKey && selectedProjectId) {
+ setLoadingLifecycle(true);
+ const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000";
+ fetch(`${apiUrl}/v1/notifications/${selectedLog.taskId}`, {
+ headers: {
+ Authorization: `Bearer ${projectApiKey}`,
+ "x-project-id": selectedProjectId,
+ },
+ })
+ .then((res) => (res.ok ? res.json() : null))
+ .then((data) => {
+ if (data && data.logs) {
+ // Sort ascending by timestamp for chronological lifecycle timeline
+ const sorted = [...data.logs].sort(
+ (a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime(),
+ );
+ setTaskLifecycle(sorted);
+ } else {
+ setTaskLifecycle([selectedLog]);
+ }
+ })
+ .catch(() => setTaskLifecycle([selectedLog]))
+ .finally(() => setLoadingLifecycle(false));
+ } else {
+ setTaskLifecycle([]);
+ }
+ }, [selectedLog, projectApiKey, selectedProjectId]);
+
+ const handleNextPage = () => {
+ if (nextCursor && projectApiKey && selectedProjectId) {
+ const newHistory = [...cursorHistory.slice(0, pageIndex + 1), nextCursor];
+ setCursorHistory(newHistory);
+ setPageIndex(pageIndex + 1);
+ void fetchLogs(projectApiKey, selectedProjectId, nextCursor);
+ }
+ };
+
+ const handlePrevPage = () => {
+ if (pageIndex > 0 && projectApiKey && selectedProjectId) {
+ const prevCursor = cursorHistory[pageIndex - 1] ?? null;
+ setPageIndex(pageIndex - 1);
+ void fetchLogs(projectApiKey, selectedProjectId, prevCursor);
+ }
+ };
+
+ return (
+
+
+
+
+
+
+ Historical Dispatches & Audit Logs
+
+
+ Comprehensive notification audit trail with payload context
+
+
+
+
+
+ {/* Filters and Search Bar */}
+
+
+
+ setSearch(e.target.value)}
+ className="w-full pl-9 pr-3 py-1.5 bg-muted/30 border border-border/40 rounded-lg text-xs font-mono focus:outline-none focus:ring-1 focus:ring-primary"
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+ Timestamp
+ Task ID
+ Template
+ Channel
+ Status
+ Details
+
+
+
+ {isLoadingProjects ? (
+
+
+ Loading projects...
+
+
+ ) : projects.length === 0 ? (
+
+
+ No projects exist. Create one in Projects & Keys tab.
+
+
+ ) : logs.length === 0 && !isLoading ? (
+
+
+ No log entries match your filter.
+
+
+ ) : (
+ Array.from(
+ logs
+ .reduce((acc, log) => {
+ if (!acc.has(log.taskId)) {
+ acc.set(log.taskId, log);
+ } else {
+ const existing = acc.get(log.taskId)!;
+ if (existing.status === "dispatched" && log.status !== "dispatched") {
+ acc.set(log.taskId, log);
+ }
+ }
+ return acc;
+ }, new Map())
+ .values(),
+ ).map((log) => (
+ setSelectedLog(log)}
+ >
+
+ {new Date(log.timestamp).toLocaleString()}
+
+
+ {log.taskId}
+
+ {log.templateId}
+
+
+ {log.channel}
+
+
+
+
+
+
+
+
+
+ ))
+ )}
+
+
+
+
+ {/* Pagination Footer */}
+
+ Page {pageIndex + 1}
+
+
+
+
+
+
+ {/* JSON Log Detail & Event Lifecycle Timeline Drawer */}
+ {selectedLog && (
+
+
+
+
+ Event Lifecycle & Detail
+
+ Task ID: {selectedLog.taskId}
+
+
+
+
+
+ {/* Complete Lifecycle Timeline */}
+
+
+
+ Notification Event Lifecycle (Dispatched → Delivered)
+
+
+ {loadingLifecycle ? (
+
+ Loading complete event lifecycle logs...
+
+ ) : taskLifecycle.length === 0 ? (
+
+ No lifecycle history recorded.
+
+ ) : (
+
+ {taskLifecycle.map((item, _idx) => {
+ const firstTime = new Date(taskLifecycle[0]!.timestamp).getTime();
+ const itemTime = new Date(item.timestamp).getTime();
+ const deltaMs = itemTime - firstTime;
+
+ return (
+
+ {/* Timeline Node Icon */}
+
+ {item.status === "delivered" ? (
+
+ ) : item.status === "failed" ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+ {item.kind === "attempt"
+ ? `Attempt #${item.attempt || 1}`
+ : item.kind}
+
+
+
+ {deltaMs > 0 ? `+${deltaMs}ms` : "Start"}
+
+
+
+
+
+ Channel:{" "}
+ {item.channel}
+
+ {new Date(item.timestamp).toLocaleTimeString()}
+
+
+ {item.providerMessageId && (
+
+ Provider Msg ID:{" "}
+ {item.providerMessageId}
+
+ )}
+
+
+ );
+ })}
+
+ )}
+
+
+ {/* Selected Record Details */}
+
+
+ Selected Log Record Metadata
+
+
+
+ Log UUID:
+ {selectedLog.id}
+
+
+ Template ID:
+ {selectedLog.templateId}
+
+
+ Provider Message ID:
+
+ {selectedLog.providerMessageId || "N/A"}
+
+
+
+ Workflow Instance:
+
+ {selectedLog.workflowInstanceId || "Direct Send"}
+
+
+
+
+
+
+
+
+ {JSON.stringify(selectedLog, null, 2)}
+
+
+
+
+ )}
+
+ );
+}
+
+function StatusBadge({ status }: { status: LogEntry["status"] }) {
+ let colorClass = "bg-gray-500/10 text-gray-400 border-gray-500/20";
+
+ switch (status) {
+ case "delivered":
+ colorClass = "bg-green-500/10 text-green-400 border-green-500/20";
+ break;
+ case "failed":
+ colorClass = "bg-red-500/10 text-red-400 border-red-500/20";
+ break;
+ case "pending":
+ case "dispatched":
+ colorClass = "bg-blue-500/10 text-blue-400 border-blue-500/20";
+ break;
+ case "canceled":
+ case "skipped":
+ colorClass = "bg-orange-500/10 text-orange-400 border-orange-500/20";
+ break;
+ }
+
+ return (
+
+ {status}
+
+ );
+}
diff --git a/dashboard/src/components/RealTimeFeed.tsx b/dashboard/src/components/RealTimeFeed.tsx
new file mode 100644
index 0000000..658a6fd
--- /dev/null
+++ b/dashboard/src/components/RealTimeFeed.tsx
@@ -0,0 +1,192 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
+import { Badge } from "@/components/ui/badge";
+import { ScrollArea } from "@/components/ui/scroll-area";
+import { CheckCircle2, XCircle, Clock, Settings2 } from "lucide-react";
+import { toast } from "sonner";
+import { useProject } from "@/hooks/useProjectKey";
+
+interface DeliveryEvent {
+ id: string;
+ type: "delivered" | "failed";
+ taskId: string;
+ channel: string;
+ error?: string;
+ providerMessageId?: string;
+ timestamp: Date;
+}
+
+export default function RealTimeFeed({
+ setIsConnected,
+}: {
+ setIsConnected: (status: boolean) => void;
+}) {
+ const [events, setEvents] = useState([]);
+ const { projects, selectedProjectId, projectApiKey, isLoadingProjects } = useProject();
+
+ useEffect(() => {
+ if (!projectApiKey || !selectedProjectId) return;
+ setIsConnected(false);
+
+ const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000";
+ const es = new EventSource(
+ `${apiUrl}/v1/events/stream?token=${projectApiKey}&projectId=${selectedProjectId}`,
+ );
+
+ es.onopen = () => {
+ setIsConnected(true);
+ toast.success("Connected to real-time feed");
+ };
+
+ es.onerror = () => {
+ setIsConnected(false);
+ };
+
+ const handleDelivered = (e: MessageEvent) => {
+ try {
+ const data = JSON.parse(e.data);
+ const newEvent: DeliveryEvent = {
+ id: crypto.randomUUID(),
+ type: "delivered",
+ taskId: data.taskId,
+ channel: data.channel,
+ providerMessageId: data.providerMessageId,
+ timestamp: new Date(),
+ };
+ setEvents((prev) => [newEvent, ...prev].slice(0, 100));
+ } catch (err) {
+ console.error("Failed to parse delivered event", err);
+ }
+ };
+
+ const handleFailed = (e: MessageEvent) => {
+ try {
+ const data = JSON.parse(e.data);
+ const newEvent: DeliveryEvent = {
+ id: crypto.randomUUID(),
+ type: "failed",
+ taskId: data.taskId,
+ channel: data.channel,
+ error: data.error,
+ timestamp: new Date(),
+ };
+ setEvents((prev) => [newEvent, ...prev].slice(0, 100));
+ } catch (err) {
+ console.error("Failed to parse failed event", err);
+ }
+ };
+
+ es.addEventListener("delivery:delivered", handleDelivered);
+ es.addEventListener("delivery:failed", handleFailed);
+
+ return () => {
+ es.removeEventListener("delivery:delivered", handleDelivered);
+ es.removeEventListener("delivery:failed", handleFailed);
+ es.close();
+ };
+ }, [projectApiKey, selectedProjectId, setIsConnected]);
+
+ return (
+
+
+
+
+
+ Live Activity Feed
+
+
+ Real-time delivery events from the notification pipeline
+
+
+
+
+ Instant Mode
+
+
+
+
+ {isLoadingProjects ? (
+
+
+ Loading projects...
+
+ ) : projects.length === 0 ? (
+
+
+ No projects exist. Create one in the Projects & Keys tab.
+
+ ) : !projectApiKey ? (
+
+
+ No API key found for this project.
+
+ ) : events.length === 0 ? (
+
+
+ Waiting for events...
+
+ ) : (
+
+ {events.map((event) => (
+
+
+
+ {event.type === "delivered" ? (
+
+ ) : (
+
+ )}
+ {event.type}
+
+
+ {event.timestamp.toLocaleTimeString()}
+
+
+
+
+
+
+ Channel
+
+
+ {event.channel}
+
+
+
+
+ Task ID
+
+
+ {event.taskId}
+
+
+
+
+ {event.error && (
+
+ {event.error}
+
+ )}
+
+ ))}
+
+ )}
+
+
+
+ );
+}
+
+function ActivityPulse() {
+ return (
+
+
+
+
+ );
+}
diff --git a/dashboard/src/components/Sidebar.tsx b/dashboard/src/components/Sidebar.tsx
new file mode 100644
index 0000000..1397f8e
--- /dev/null
+++ b/dashboard/src/components/Sidebar.tsx
@@ -0,0 +1,81 @@
+"use client";
+
+import Link from "next/link";
+import { usePathname } from "next/navigation";
+import {
+ Activity,
+ History,
+ FolderKey,
+ Users,
+ LayoutDashboard,
+ HeartPulse,
+ BarChart3,
+ AlertTriangle,
+ Clock,
+ GitFork,
+ FileCode2,
+} from "lucide-react";
+import { cn } from "@/lib/utils";
+
+const navigation = [
+ { name: "Live Feed", href: "/", icon: Activity },
+ { name: "History Logs", href: "/logs", icon: History },
+ { name: "System Health", href: "/system", icon: HeartPulse },
+ { name: "Analytics & Queue", href: "/analytics", icon: BarChart3 },
+ { name: "Dead Letter (DLQ)", href: "/dlq", icon: AlertTriangle },
+ { name: "Scheduled", href: "/scheduled", icon: Clock },
+ { name: "Workflows", href: "/workflows", icon: GitFork },
+ { name: "Templates", href: "/templates", icon: FileCode2 },
+ { name: "Users", href: "/users", icon: Users },
+ { name: "Projects & Keys", href: "/projects", icon: FolderKey },
+];
+
+export default function Sidebar() {
+ const pathname = usePathname();
+
+ return (
+
+
+
+
+
+
+ Notifkit Observability v1.0
+
+
+ );
+}
diff --git a/dashboard/src/components/ui/badge.tsx b/dashboard/src/components/ui/badge.tsx
new file mode 100644
index 0000000..e1997af
--- /dev/null
+++ b/dashboard/src/components/ui/badge.tsx
@@ -0,0 +1,49 @@
+import { mergeProps } from "@base-ui/react/merge-props";
+import { useRender } from "@base-ui/react/use-render";
+import { cva, type VariantProps } from "class-variance-authority";
+
+import { cn } from "@/lib/utils";
+
+const badgeVariants = cva(
+ "group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
+ {
+ variants: {
+ variant: {
+ default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
+ secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
+ destructive:
+ "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
+ outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
+ ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
+ link: "text-primary underline-offset-4 hover:underline",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ },
+);
+
+function Badge({
+ className,
+ variant = "default",
+ render,
+ ...props
+}: useRender.ComponentProps<"span"> & VariantProps) {
+ return useRender({
+ defaultTagName: "span",
+ props: mergeProps<"span">(
+ {
+ className: cn(badgeVariants({ variant }), className),
+ },
+ props,
+ ),
+ render,
+ state: {
+ slot: "badge",
+ variant,
+ },
+ });
+}
+
+export { Badge, badgeVariants };
diff --git a/dashboard/src/components/ui/button.tsx b/dashboard/src/components/ui/button.tsx
new file mode 100644
index 0000000..e2ee793
--- /dev/null
+++ b/dashboard/src/components/ui/button.tsx
@@ -0,0 +1,58 @@
+import { Button as ButtonPrimitive } from "@base-ui/react/button";
+import { cva, type VariantProps } from "class-variance-authority";
+
+import { cn } from "@/lib/utils";
+
+const buttonVariants = cva(
+ "group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
+ {
+ variants: {
+ variant: {
+ default: "bg-primary text-primary-foreground hover:bg-primary/80",
+ outline:
+ "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
+ secondary:
+ "bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
+ ghost:
+ "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
+ destructive:
+ "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
+ link: "text-primary underline-offset-4 hover:underline",
+ },
+ size: {
+ default:
+ "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
+ xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
+ sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
+ lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
+ icon: "size-8",
+ "icon-xs":
+ "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
+ "icon-sm":
+ "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
+ "icon-lg": "size-9",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ },
+);
+
+function Button({
+ className,
+ variant = "default",
+ size = "default",
+ ...props
+}: ButtonPrimitive.Props & VariantProps) {
+ return (
+
+ );
+}
+
+export { Button, buttonVariants };
diff --git a/dashboard/src/components/ui/card.tsx b/dashboard/src/components/ui/card.tsx
new file mode 100644
index 0000000..a73bb76
--- /dev/null
+++ b/dashboard/src/components/ui/card.tsx
@@ -0,0 +1,88 @@
+import * as React from "react";
+
+import { cn } from "@/lib/utils";
+
+function Card({
+ className,
+ size = "default",
+ ...props
+}: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
+ return (
+ img:first-child]:pt-0 data-[size=sm]:[--card-spacing:--spacing(3)] data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
+ className,
+ )}
+ {...props}
+ />
+ );
+}
+
+function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
+
+function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
+
+function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
+
+function CardAction({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
+
+function CardContent({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
+
+function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ );
+}
+
+export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent };
diff --git a/dashboard/src/components/ui/scroll-area.tsx b/dashboard/src/components/ui/scroll-area.tsx
new file mode 100644
index 0000000..43ecbda
--- /dev/null
+++ b/dashboard/src/components/ui/scroll-area.tsx
@@ -0,0 +1,51 @@
+"use client";
+
+import * as React from "react";
+import { ScrollArea as ScrollAreaPrimitive } from "@base-ui/react/scroll-area";
+
+import { cn } from "@/lib/utils";
+
+function ScrollArea({ className, children, ...props }: ScrollAreaPrimitive.Root.Props) {
+ return (
+
+
+ {children}
+
+
+
+
+ );
+}
+
+function ScrollBar({
+ className,
+ orientation = "vertical",
+ ...props
+}: ScrollAreaPrimitive.Scrollbar.Props) {
+ return (
+
+
+
+ );
+}
+
+export { ScrollArea, ScrollBar };
diff --git a/dashboard/src/components/ui/sonner.tsx b/dashboard/src/components/ui/sonner.tsx
new file mode 100644
index 0000000..7cbe96b
--- /dev/null
+++ b/dashboard/src/components/ui/sonner.tsx
@@ -0,0 +1,45 @@
+"use client";
+
+import { useTheme } from "next-themes";
+import { Toaster as Sonner, type ToasterProps } from "sonner";
+import {
+ CircleCheckIcon,
+ InfoIcon,
+ TriangleAlertIcon,
+ OctagonXIcon,
+ Loader2Icon,
+} from "lucide-react";
+
+const Toaster = ({ ...props }: ToasterProps) => {
+ const { theme = "system" } = useTheme();
+
+ return (
+ ,
+ info: ,
+ warning: ,
+ error: ,
+ loading: ,
+ }}
+ style={
+ {
+ "--normal-bg": "var(--popover)",
+ "--normal-text": "var(--popover-foreground)",
+ "--normal-border": "var(--border)",
+ "--border-radius": "var(--radius)",
+ } as React.CSSProperties
+ }
+ toastOptions={{
+ classNames: {
+ toast: "cn-toast",
+ },
+ }}
+ {...props}
+ />
+ );
+};
+
+export { Toaster };
diff --git a/dashboard/src/components/ui/table.tsx b/dashboard/src/components/ui/table.tsx
new file mode 100644
index 0000000..86d90c8
--- /dev/null
+++ b/dashboard/src/components/ui/table.tsx
@@ -0,0 +1,89 @@
+"use client";
+
+import * as React from "react";
+
+import { cn } from "@/lib/utils";
+
+function Table({ className, ...props }: React.ComponentProps<"table">) {
+ return (
+
+ );
+}
+
+function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
+ return ;
+}
+
+function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
+ return (
+
+ );
+}
+
+function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
+ return (
+ tr]:last:border-b-0", className)}
+ {...props}
+ />
+ );
+}
+
+function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
+ return (
+
+ );
+}
+
+function TableHead({ className, ...props }: React.ComponentProps<"th">) {
+ return (
+ |
+ );
+}
+
+function TableCell({ className, ...props }: React.ComponentProps<"td">) {
+ return (
+ |
+ );
+}
+
+function TableCaption({ className, ...props }: React.ComponentProps<"caption">) {
+ return (
+
+ );
+}
+
+export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption };
diff --git a/dashboard/src/hooks/useProjectKey.tsx b/dashboard/src/hooks/useProjectKey.tsx
new file mode 100644
index 0000000..9ad4916
--- /dev/null
+++ b/dashboard/src/hooks/useProjectKey.tsx
@@ -0,0 +1,69 @@
+"use client";
+
+import type { ReactNode } from "react";
+import { createContext, useContext, useState, useEffect } from "react";
+
+interface ProjectContextType {
+ projects: any[];
+ selectedProjectId: string;
+ setSelectedProjectId: (id: string) => void;
+ projectApiKey: string | null;
+ isLoadingProjects: boolean;
+}
+
+const ProjectContext = createContext(undefined);
+
+export function ProjectProvider({ children }: { children: ReactNode }) {
+ const [projects, setProjects] = useState([]);
+ const [selectedProjectId, setSelectedProjectId] = useState("");
+ const [projectApiKey, setProjectApiKey] = useState(null);
+ const [isLoadingProjects, setIsLoadingProjects] = useState(true);
+
+ const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000";
+ const adminKey = process.env.NEXT_PUBLIC_ADMIN_KEY || "test_admin_key";
+
+ useEffect(() => {
+ fetch(`${apiUrl}/v1/projects`, { headers: { Authorization: `Bearer ${adminKey}` } })
+ .then((res) => res.json())
+ .then((data) => {
+ const projs = data.projects || [];
+ setProjects(projs);
+ if (projs.length > 0) {
+ setSelectedProjectId(projs[0].id);
+ }
+ setIsLoadingProjects(false);
+ })
+ .catch((err) => {
+ console.error(err);
+ setIsLoadingProjects(false);
+ });
+ }, [apiUrl, adminKey]);
+
+ useEffect(() => {
+ // With Admin token, we no longer need the project's API key.
+ // We just use the Admin token and pass x-project-id.
+ setProjectApiKey(adminKey);
+ }, [adminKey]);
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useProject() {
+ const context = useContext(ProjectContext);
+ if (context === undefined) {
+ throw new Error("useProject must be used within a ProjectProvider");
+ }
+ return context;
+}
diff --git a/dashboard/src/lib/utils.ts b/dashboard/src/lib/utils.ts
new file mode 100644
index 0000000..a5ef193
--- /dev/null
+++ b/dashboard/src/lib/utils.ts
@@ -0,0 +1,6 @@
+import { clsx, type ClassValue } from "clsx";
+import { twMerge } from "tailwind-merge";
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs));
+}
diff --git a/dashboard/tsconfig.json b/dashboard/tsconfig.json
new file mode 100644
index 0000000..cf9c65d
--- /dev/null
+++ b/dashboard/tsconfig.json
@@ -0,0 +1,34 @@
+{
+ "compilerOptions": {
+ "target": "ES2017",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "react-jsx",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ ".next/types/**/*.ts",
+ ".next/dev/types/**/*.ts",
+ "**/*.mts"
+ ],
+ "exclude": ["node_modules"]
+}
diff --git a/docker/.gitkeep b/docker/.gitkeep
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/docker/.gitkeep
@@ -0,0 +1 @@
+
diff --git a/drizzle.config.ts b/drizzle.config.ts
new file mode 100644
index 0000000..5e7ae9a
--- /dev/null
+++ b/drizzle.config.ts
@@ -0,0 +1,10 @@
+import { defineConfig } from "drizzle-kit";
+
+export default defineConfig({
+ schema: "./src/db/schema.ts",
+ out: "./drizzle",
+ dialect: "postgresql",
+ dbCredentials: {
+ url: process.env.DATABASE_URL || "postgres://platform:platform@localhost:5432/notifkit",
+ },
+});
diff --git a/drizzle/0000_spotty_jack_flag.sql b/drizzle/0000_spotty_jack_flag.sql
new file mode 100644
index 0000000..511ab10
--- /dev/null
+++ b/drizzle/0000_spotty_jack_flag.sql
@@ -0,0 +1,189 @@
+CREATE TYPE "public"."api_key_role" AS ENUM('admin', 'read_only');--> statement-breakpoint
+CREATE TYPE "public"."channel" AS ENUM('email', 'sms', 'push', 'webhook', 'in-app');--> statement-breakpoint
+CREATE TYPE "public"."workflow_status" AS ENUM('pending', 'running', 'completed', 'failed');--> statement-breakpoint
+CREATE TABLE "contact_topic_preferences" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "contact_id" uuid NOT NULL,
+ "topic" varchar NOT NULL,
+ "enabled" boolean NOT NULL,
+ CONSTRAINT "contact_topic_preferences_contact_id_topic_unique" UNIQUE("contact_id","topic")
+);
+--> statement-breakpoint
+CREATE TABLE "delivery_outbox" (
+ "task_id" varchar NOT NULL,
+ "channel" "channel" NOT NULL,
+ "destination" text NOT NULL,
+ "provider_message_id" varchar,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ CONSTRAINT "delivery_outbox_task_id_channel_destination_pk" PRIMARY KEY("task_id","channel","destination")
+);
+--> statement-breakpoint
+CREATE TABLE "message_logs" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "project_id" uuid NOT NULL,
+ "task_id" varchar NOT NULL,
+ "provider_message_id" varchar,
+ "template_id" varchar,
+ "workflow_instance_id" uuid,
+ "channel" "channel" NOT NULL,
+ "attempt" integer DEFAULT 1 NOT NULL,
+ "kind" varchar DEFAULT 'attempt' NOT NULL,
+ "status" varchar NOT NULL,
+ "timestamp" timestamp with time zone DEFAULT now() NOT NULL,
+ CONSTRAINT "task_channel_attempt_uidx" UNIQUE("task_id","channel","attempt","kind")
+);
+--> statement-breakpoint
+CREATE TABLE "project_api_keys" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "project_id" uuid NOT NULL,
+ "key_hash" varchar NOT NULL,
+ "role" "api_key_role" DEFAULT 'admin' NOT NULL,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ CONSTRAINT "project_api_keys_key_hash_unique" UNIQUE("key_hash")
+);
+--> statement-breakpoint
+CREATE TABLE "projects" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "name" varchar NOT NULL,
+ "rate_limit_rpm" integer,
+ "throttle_limit" integer,
+ "throttle_window_hours" integer,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL
+);
+--> statement-breakpoint
+CREATE TABLE "quiet_hours" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "user_id" uuid,
+ "contact_id" uuid,
+ "start_time" time NOT NULL,
+ "end_time" time NOT NULL,
+ CONSTRAINT "check_owner" CHECK (num_nonnulls(user_id, contact_id) = 1)
+);
+--> statement-breakpoint
+CREATE TABLE "scheduled_payloads" (
+ "task_id" varchar PRIMARY KEY NOT NULL,
+ "payload" jsonb NOT NULL,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL
+);
+--> statement-breakpoint
+CREATE TABLE "templates" (
+ "project_id" uuid NOT NULL,
+ "id" varchar NOT NULL,
+ "channel" "channel" NOT NULL,
+ "topics" text[] NOT NULL,
+ "content" jsonb NOT NULL,
+ "ai_prompts" jsonb,
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL,
+ CONSTRAINT "templates_project_id_id_pk" PRIMARY KEY("project_id","id")
+);
+--> statement-breakpoint
+CREATE TABLE "user_channel_preferences" (
+ "user_id" uuid NOT NULL,
+ "channel" "channel" NOT NULL,
+ "enabled" boolean NOT NULL,
+ CONSTRAINT "user_channel_preferences_user_id_channel_pk" PRIMARY KEY("user_id","channel")
+);
+--> statement-breakpoint
+CREATE TABLE "user_contacts" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "user_id" uuid NOT NULL,
+ "channel" "channel" NOT NULL,
+ "target" text NOT NULL,
+ "label" text,
+ "is_primary" boolean DEFAULT false NOT NULL,
+ "enabled" boolean DEFAULT true NOT NULL,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ CONSTRAINT "user_contacts_user_id_channel_target_unique" UNIQUE("user_id","channel","target")
+);
+--> statement-breakpoint
+CREATE TABLE "user_segments" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "user_id" uuid NOT NULL,
+ "segment" varchar NOT NULL,
+ CONSTRAINT "user_segments_user_id_segment_unique" UNIQUE("user_id","segment")
+);
+--> statement-breakpoint
+CREATE TABLE "user_topic_preferences" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "user_id" uuid NOT NULL,
+ "topic" varchar NOT NULL,
+ "enabled" boolean NOT NULL,
+ CONSTRAINT "user_topic_preferences_user_id_topic_unique" UNIQUE("user_id","topic")
+);
+--> statement-breakpoint
+CREATE TABLE "users" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "project_id" uuid NOT NULL,
+ "external_id" text NOT NULL,
+ "attributes" jsonb DEFAULT '{}'::jsonb NOT NULL,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL,
+ CONSTRAINT "users_project_id_external_id_unique" UNIQUE("project_id","external_id")
+);
+--> statement-breakpoint
+CREATE TABLE "workflow_definitions" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "project_id" uuid NOT NULL,
+ "name" varchar NOT NULL,
+ "steps" jsonb NOT NULL,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ CONSTRAINT "workflow_definitions_project_id_name_unique" UNIQUE("project_id","name")
+);
+--> statement-breakpoint
+CREATE TABLE "workflow_instances" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "project_id" uuid NOT NULL,
+ "name" varchar NOT NULL,
+ "status" "workflow_status" DEFAULT 'pending' NOT NULL,
+ "input" jsonb DEFAULT '{}'::jsonb,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL
+);
+--> statement-breakpoint
+CREATE TABLE "workflow_steps" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "project_id" uuid NOT NULL,
+ "instance_id" uuid NOT NULL,
+ "step_index" varchar NOT NULL,
+ "action" varchar NOT NULL,
+ "output" jsonb,
+ "error" text,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ CONSTRAINT "workflow_steps_instance_id_step_index_unique" UNIQUE("instance_id","step_index")
+);
+--> statement-breakpoint
+CREATE TABLE "workflow_waiters" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "project_id" uuid NOT NULL,
+ "instance_id" uuid NOT NULL,
+ "event_name" varchar NOT NULL,
+ "match_criteria" jsonb NOT NULL,
+ "expires_at" timestamp with time zone NOT NULL,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL
+);
+--> statement-breakpoint
+ALTER TABLE "contact_topic_preferences" ADD CONSTRAINT "contact_topic_preferences_contact_id_user_contacts_id_fk" FOREIGN KEY ("contact_id") REFERENCES "public"."user_contacts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "project_api_keys" ADD CONSTRAINT "project_api_keys_project_id_projects_id_fk" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "quiet_hours" ADD CONSTRAINT "quiet_hours_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "quiet_hours" ADD CONSTRAINT "quiet_hours_contact_id_user_contacts_id_fk" FOREIGN KEY ("contact_id") REFERENCES "public"."user_contacts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "templates" ADD CONSTRAINT "templates_project_id_projects_id_fk" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "user_channel_preferences" ADD CONSTRAINT "user_channel_preferences_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "user_contacts" ADD CONSTRAINT "user_contacts_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "user_segments" ADD CONSTRAINT "user_segments_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "user_topic_preferences" ADD CONSTRAINT "user_topic_preferences_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "workflow_steps" ADD CONSTRAINT "workflow_steps_instance_id_workflow_instances_id_fk" FOREIGN KEY ("instance_id") REFERENCES "public"."workflow_instances"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "workflow_waiters" ADD CONSTRAINT "workflow_waiters_instance_id_workflow_instances_id_fk" FOREIGN KEY ("instance_id") REFERENCES "public"."workflow_instances"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+CREATE INDEX "message_logs_project_idx" ON "message_logs" USING btree ("project_id");--> statement-breakpoint
+CREATE INDEX "task_idx" ON "message_logs" USING btree ("task_id");--> statement-breakpoint
+CREATE INDEX "message_logs_project_task_idx" ON "message_logs" USING btree ("project_id","task_id");--> statement-breakpoint
+CREATE INDEX "provider_msg_idx" ON "message_logs" USING btree ("provider_message_id");--> statement-breakpoint
+CREATE INDEX "msg_log_proj_time_idx" ON "message_logs" USING btree ("project_id","timestamp");--> statement-breakpoint
+CREATE INDEX "msg_log_template_idx" ON "message_logs" USING btree ("project_id","template_id");--> statement-breakpoint
+CREATE INDEX "msg_log_workflow_idx" ON "message_logs" USING btree ("project_id","workflow_instance_id");--> statement-breakpoint
+CREATE INDEX "quiet_hours_user_idx" ON "quiet_hours" USING btree ("user_id");--> statement-breakpoint
+CREATE INDEX "segment_idx" ON "user_segments" USING btree ("segment");--> statement-breakpoint
+CREATE INDEX "workflow_name_idx" ON "workflow_instances" USING btree ("name");--> statement-breakpoint
+CREATE INDEX "waiter_event_idx" ON "workflow_waiters" USING btree ("event_name");--> statement-breakpoint
+CREATE INDEX "waiter_instance_idx" ON "workflow_waiters" USING btree ("instance_id");--> statement-breakpoint
+CREATE INDEX "waiter_comp_idx" ON "workflow_waiters" USING btree ("event_name","project_id","expires_at");--> statement-breakpoint
+CREATE INDEX "waiter_match_idx" ON "workflow_waiters" USING gin ("match_criteria");
\ No newline at end of file
diff --git a/drizzle/0001_stale_shotgun.sql b/drizzle/0001_stale_shotgun.sql
new file mode 100644
index 0000000..c34f0e1
--- /dev/null
+++ b/drizzle/0001_stale_shotgun.sql
@@ -0,0 +1,17 @@
+CREATE TABLE "suppressions" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "project_id" uuid NOT NULL,
+ "channel" "channel" NOT NULL,
+ "target" varchar NOT NULL,
+ "reason" varchar NOT NULL,
+ "source" varchar,
+ "task_id" varchar,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ CONSTRAINT "suppression_project_channel_target_uidx" UNIQUE("project_id","channel","target")
+);
+--> statement-breakpoint
+ALTER TABLE "message_logs" ADD COLUMN "campaign_id" varchar;--> statement-breakpoint
+ALTER TABLE "message_logs" ADD COLUMN "metadata" jsonb;--> statement-breakpoint
+CREATE INDEX "suppression_project_idx" ON "suppressions" USING btree ("project_id");--> statement-breakpoint
+CREATE INDEX "suppression_lookup_idx" ON "suppressions" USING btree ("project_id","channel","target");--> statement-breakpoint
+CREATE INDEX "msg_log_campaign_idx" ON "message_logs" USING btree ("project_id","campaign_id");
\ No newline at end of file
diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json
new file mode 100644
index 0000000..a795b9c
--- /dev/null
+++ b/drizzle/meta/0000_snapshot.json
@@ -0,0 +1,1314 @@
+{
+ "id": "4758cb06-bdf3-4c4f-883a-0cabdade32ae",
+ "prevId": "00000000-0000-0000-0000-000000000000",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.contact_topic_preferences": {
+ "name": "contact_topic_preferences",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "contact_id": {
+ "name": "contact_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "topic": {
+ "name": "topic",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "contact_topic_preferences_contact_id_user_contacts_id_fk": {
+ "name": "contact_topic_preferences_contact_id_user_contacts_id_fk",
+ "tableFrom": "contact_topic_preferences",
+ "tableTo": "user_contacts",
+ "columnsFrom": ["contact_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "contact_topic_preferences_contact_id_topic_unique": {
+ "name": "contact_topic_preferences_contact_id_topic_unique",
+ "nullsNotDistinct": false,
+ "columns": ["contact_id", "topic"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.delivery_outbox": {
+ "name": "delivery_outbox",
+ "schema": "",
+ "columns": {
+ "task_id": {
+ "name": "task_id",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel": {
+ "name": "channel",
+ "type": "channel",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "destination": {
+ "name": "destination",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_message_id": {
+ "name": "provider_message_id",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "delivery_outbox_task_id_channel_destination_pk": {
+ "name": "delivery_outbox_task_id_channel_destination_pk",
+ "columns": ["task_id", "channel", "destination"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.message_logs": {
+ "name": "message_logs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "task_id": {
+ "name": "task_id",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_message_id": {
+ "name": "provider_message_id",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "template_id": {
+ "name": "template_id",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "workflow_instance_id": {
+ "name": "workflow_instance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "channel": {
+ "name": "channel",
+ "type": "channel",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "attempt": {
+ "name": "attempt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "kind": {
+ "name": "kind",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'attempt'"
+ },
+ "status": {
+ "name": "status",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "timestamp": {
+ "name": "timestamp",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "message_logs_project_idx": {
+ "name": "message_logs_project_idx",
+ "columns": [
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "task_idx": {
+ "name": "task_idx",
+ "columns": [
+ {
+ "expression": "task_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "message_logs_project_task_idx": {
+ "name": "message_logs_project_task_idx",
+ "columns": [
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "task_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "provider_msg_idx": {
+ "name": "provider_msg_idx",
+ "columns": [
+ {
+ "expression": "provider_message_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "msg_log_proj_time_idx": {
+ "name": "msg_log_proj_time_idx",
+ "columns": [
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "timestamp",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "msg_log_template_idx": {
+ "name": "msg_log_template_idx",
+ "columns": [
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "template_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "msg_log_workflow_idx": {
+ "name": "msg_log_workflow_idx",
+ "columns": [
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "workflow_instance_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "task_channel_attempt_uidx": {
+ "name": "task_channel_attempt_uidx",
+ "nullsNotDistinct": false,
+ "columns": ["task_id", "channel", "attempt", "kind"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.project_api_keys": {
+ "name": "project_api_keys",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key_hash": {
+ "name": "key_hash",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "api_key_role",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'admin'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "project_api_keys_project_id_projects_id_fk": {
+ "name": "project_api_keys_project_id_projects_id_fk",
+ "tableFrom": "project_api_keys",
+ "tableTo": "projects",
+ "columnsFrom": ["project_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "project_api_keys_key_hash_unique": {
+ "name": "project_api_keys_key_hash_unique",
+ "nullsNotDistinct": false,
+ "columns": ["key_hash"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.projects": {
+ "name": "projects",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "rate_limit_rpm": {
+ "name": "rate_limit_rpm",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "throttle_limit": {
+ "name": "throttle_limit",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "throttle_window_hours": {
+ "name": "throttle_window_hours",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.quiet_hours": {
+ "name": "quiet_hours",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "contact_id": {
+ "name": "contact_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "start_time": {
+ "name": "start_time",
+ "type": "time",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "end_time": {
+ "name": "end_time",
+ "type": "time",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "quiet_hours_user_idx": {
+ "name": "quiet_hours_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "quiet_hours_user_id_users_id_fk": {
+ "name": "quiet_hours_user_id_users_id_fk",
+ "tableFrom": "quiet_hours",
+ "tableTo": "users",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "quiet_hours_contact_id_user_contacts_id_fk": {
+ "name": "quiet_hours_contact_id_user_contacts_id_fk",
+ "tableFrom": "quiet_hours",
+ "tableTo": "user_contacts",
+ "columnsFrom": ["contact_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "check_owner": {
+ "name": "check_owner",
+ "value": "num_nonnulls(user_id, contact_id) = 1"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.scheduled_payloads": {
+ "name": "scheduled_payloads",
+ "schema": "",
+ "columns": {
+ "task_id": {
+ "name": "task_id",
+ "type": "varchar",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "payload": {
+ "name": "payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.templates": {
+ "name": "templates",
+ "schema": "",
+ "columns": {
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "id": {
+ "name": "id",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel": {
+ "name": "channel",
+ "type": "channel",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "topics": {
+ "name": "topics",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content": {
+ "name": "content",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ai_prompts": {
+ "name": "ai_prompts",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "templates_project_id_projects_id_fk": {
+ "name": "templates_project_id_projects_id_fk",
+ "tableFrom": "templates",
+ "tableTo": "projects",
+ "columnsFrom": ["project_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "templates_project_id_id_pk": {
+ "name": "templates_project_id_id_pk",
+ "columns": ["project_id", "id"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_channel_preferences": {
+ "name": "user_channel_preferences",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel": {
+ "name": "channel",
+ "type": "channel",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "user_channel_preferences_user_id_users_id_fk": {
+ "name": "user_channel_preferences_user_id_users_id_fk",
+ "tableFrom": "user_channel_preferences",
+ "tableTo": "users",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "user_channel_preferences_user_id_channel_pk": {
+ "name": "user_channel_preferences_user_id_channel_pk",
+ "columns": ["user_id", "channel"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_contacts": {
+ "name": "user_contacts",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel": {
+ "name": "channel",
+ "type": "channel",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target": {
+ "name": "target",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "label": {
+ "name": "label",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_primary": {
+ "name": "is_primary",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "user_contacts_user_id_users_id_fk": {
+ "name": "user_contacts_user_id_users_id_fk",
+ "tableFrom": "user_contacts",
+ "tableTo": "users",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_contacts_user_id_channel_target_unique": {
+ "name": "user_contacts_user_id_channel_target_unique",
+ "nullsNotDistinct": false,
+ "columns": ["user_id", "channel", "target"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_segments": {
+ "name": "user_segments",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "segment": {
+ "name": "segment",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "segment_idx": {
+ "name": "segment_idx",
+ "columns": [
+ {
+ "expression": "segment",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "user_segments_user_id_users_id_fk": {
+ "name": "user_segments_user_id_users_id_fk",
+ "tableFrom": "user_segments",
+ "tableTo": "users",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_segments_user_id_segment_unique": {
+ "name": "user_segments_user_id_segment_unique",
+ "nullsNotDistinct": false,
+ "columns": ["user_id", "segment"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_topic_preferences": {
+ "name": "user_topic_preferences",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "topic": {
+ "name": "topic",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "user_topic_preferences_user_id_users_id_fk": {
+ "name": "user_topic_preferences_user_id_users_id_fk",
+ "tableFrom": "user_topic_preferences",
+ "tableTo": "users",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_topic_preferences_user_id_topic_unique": {
+ "name": "user_topic_preferences_user_id_topic_unique",
+ "nullsNotDistinct": false,
+ "columns": ["user_id", "topic"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.users": {
+ "name": "users",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "attributes": {
+ "name": "attributes",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "users_project_id_external_id_unique": {
+ "name": "users_project_id_external_id_unique",
+ "nullsNotDistinct": false,
+ "columns": ["project_id", "external_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.workflow_definitions": {
+ "name": "workflow_definitions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "steps": {
+ "name": "steps",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "workflow_definitions_project_id_name_unique": {
+ "name": "workflow_definitions_project_id_name_unique",
+ "nullsNotDistinct": false,
+ "columns": ["project_id", "name"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.workflow_instances": {
+ "name": "workflow_instances",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "workflow_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "input": {
+ "name": "input",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{}'::jsonb"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "workflow_name_idx": {
+ "name": "workflow_name_idx",
+ "columns": [
+ {
+ "expression": "name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.workflow_steps": {
+ "name": "workflow_steps",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "instance_id": {
+ "name": "instance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "step_index": {
+ "name": "step_index",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action": {
+ "name": "action",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "output": {
+ "name": "output",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error": {
+ "name": "error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "workflow_steps_instance_id_workflow_instances_id_fk": {
+ "name": "workflow_steps_instance_id_workflow_instances_id_fk",
+ "tableFrom": "workflow_steps",
+ "tableTo": "workflow_instances",
+ "columnsFrom": ["instance_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "workflow_steps_instance_id_step_index_unique": {
+ "name": "workflow_steps_instance_id_step_index_unique",
+ "nullsNotDistinct": false,
+ "columns": ["instance_id", "step_index"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.workflow_waiters": {
+ "name": "workflow_waiters",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "instance_id": {
+ "name": "instance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_name": {
+ "name": "event_name",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "match_criteria": {
+ "name": "match_criteria",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "waiter_event_idx": {
+ "name": "waiter_event_idx",
+ "columns": [
+ {
+ "expression": "event_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "waiter_instance_idx": {
+ "name": "waiter_instance_idx",
+ "columns": [
+ {
+ "expression": "instance_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "waiter_comp_idx": {
+ "name": "waiter_comp_idx",
+ "columns": [
+ {
+ "expression": "event_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "waiter_match_idx": {
+ "name": "waiter_match_idx",
+ "columns": [
+ {
+ "expression": "match_criteria",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "gin",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "workflow_waiters_instance_id_workflow_instances_id_fk": {
+ "name": "workflow_waiters_instance_id_workflow_instances_id_fk",
+ "tableFrom": "workflow_waiters",
+ "tableTo": "workflow_instances",
+ "columnsFrom": ["instance_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {
+ "public.api_key_role": {
+ "name": "api_key_role",
+ "schema": "public",
+ "values": ["admin", "read_only"]
+ },
+ "public.channel": {
+ "name": "channel",
+ "schema": "public",
+ "values": ["email", "sms", "push", "webhook", "in-app"]
+ },
+ "public.workflow_status": {
+ "name": "workflow_status",
+ "schema": "public",
+ "values": ["pending", "running", "completed", "failed"]
+ }
+ },
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
diff --git a/drizzle/meta/0001_snapshot.json b/drizzle/meta/0001_snapshot.json
new file mode 100644
index 0000000..6aa0904
--- /dev/null
+++ b/drizzle/meta/0001_snapshot.json
@@ -0,0 +1,1460 @@
+{
+ "id": "4d282198-10d4-4fbe-88e5-3df20c511678",
+ "prevId": "4758cb06-bdf3-4c4f-883a-0cabdade32ae",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.contact_topic_preferences": {
+ "name": "contact_topic_preferences",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "contact_id": {
+ "name": "contact_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "topic": {
+ "name": "topic",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "contact_topic_preferences_contact_id_user_contacts_id_fk": {
+ "name": "contact_topic_preferences_contact_id_user_contacts_id_fk",
+ "tableFrom": "contact_topic_preferences",
+ "tableTo": "user_contacts",
+ "columnsFrom": ["contact_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "contact_topic_preferences_contact_id_topic_unique": {
+ "name": "contact_topic_preferences_contact_id_topic_unique",
+ "nullsNotDistinct": false,
+ "columns": ["contact_id", "topic"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.delivery_outbox": {
+ "name": "delivery_outbox",
+ "schema": "",
+ "columns": {
+ "task_id": {
+ "name": "task_id",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel": {
+ "name": "channel",
+ "type": "channel",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "destination": {
+ "name": "destination",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_message_id": {
+ "name": "provider_message_id",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "delivery_outbox_task_id_channel_destination_pk": {
+ "name": "delivery_outbox_task_id_channel_destination_pk",
+ "columns": ["task_id", "channel", "destination"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.message_logs": {
+ "name": "message_logs",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "task_id": {
+ "name": "task_id",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_message_id": {
+ "name": "provider_message_id",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "template_id": {
+ "name": "template_id",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "workflow_instance_id": {
+ "name": "workflow_instance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "channel": {
+ "name": "channel",
+ "type": "channel",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "attempt": {
+ "name": "attempt",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "kind": {
+ "name": "kind",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'attempt'"
+ },
+ "status": {
+ "name": "status",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "campaign_id": {
+ "name": "campaign_id",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "timestamp": {
+ "name": "timestamp",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "message_logs_project_idx": {
+ "name": "message_logs_project_idx",
+ "columns": [
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "task_idx": {
+ "name": "task_idx",
+ "columns": [
+ {
+ "expression": "task_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "message_logs_project_task_idx": {
+ "name": "message_logs_project_task_idx",
+ "columns": [
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "task_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "provider_msg_idx": {
+ "name": "provider_msg_idx",
+ "columns": [
+ {
+ "expression": "provider_message_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "msg_log_proj_time_idx": {
+ "name": "msg_log_proj_time_idx",
+ "columns": [
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "timestamp",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "msg_log_template_idx": {
+ "name": "msg_log_template_idx",
+ "columns": [
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "template_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "msg_log_workflow_idx": {
+ "name": "msg_log_workflow_idx",
+ "columns": [
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "workflow_instance_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "msg_log_campaign_idx": {
+ "name": "msg_log_campaign_idx",
+ "columns": [
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "campaign_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "task_channel_attempt_uidx": {
+ "name": "task_channel_attempt_uidx",
+ "nullsNotDistinct": false,
+ "columns": ["task_id", "channel", "attempt", "kind"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.project_api_keys": {
+ "name": "project_api_keys",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "key_hash": {
+ "name": "key_hash",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "api_key_role",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'admin'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "project_api_keys_project_id_projects_id_fk": {
+ "name": "project_api_keys_project_id_projects_id_fk",
+ "tableFrom": "project_api_keys",
+ "tableTo": "projects",
+ "columnsFrom": ["project_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "project_api_keys_key_hash_unique": {
+ "name": "project_api_keys_key_hash_unique",
+ "nullsNotDistinct": false,
+ "columns": ["key_hash"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.projects": {
+ "name": "projects",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "rate_limit_rpm": {
+ "name": "rate_limit_rpm",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "throttle_limit": {
+ "name": "throttle_limit",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "throttle_window_hours": {
+ "name": "throttle_window_hours",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.quiet_hours": {
+ "name": "quiet_hours",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "contact_id": {
+ "name": "contact_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "start_time": {
+ "name": "start_time",
+ "type": "time",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "end_time": {
+ "name": "end_time",
+ "type": "time",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "quiet_hours_user_idx": {
+ "name": "quiet_hours_user_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "quiet_hours_user_id_users_id_fk": {
+ "name": "quiet_hours_user_id_users_id_fk",
+ "tableFrom": "quiet_hours",
+ "tableTo": "users",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "quiet_hours_contact_id_user_contacts_id_fk": {
+ "name": "quiet_hours_contact_id_user_contacts_id_fk",
+ "tableFrom": "quiet_hours",
+ "tableTo": "user_contacts",
+ "columnsFrom": ["contact_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {
+ "check_owner": {
+ "name": "check_owner",
+ "value": "num_nonnulls(user_id, contact_id) = 1"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "public.scheduled_payloads": {
+ "name": "scheduled_payloads",
+ "schema": "",
+ "columns": {
+ "task_id": {
+ "name": "task_id",
+ "type": "varchar",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "payload": {
+ "name": "payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.suppressions": {
+ "name": "suppressions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel": {
+ "name": "channel",
+ "type": "channel",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target": {
+ "name": "target",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reason": {
+ "name": "reason",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "source": {
+ "name": "source",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "task_id": {
+ "name": "task_id",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "suppression_project_idx": {
+ "name": "suppression_project_idx",
+ "columns": [
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "suppression_lookup_idx": {
+ "name": "suppression_lookup_idx",
+ "columns": [
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "channel",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "target",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "suppression_project_channel_target_uidx": {
+ "name": "suppression_project_channel_target_uidx",
+ "nullsNotDistinct": false,
+ "columns": ["project_id", "channel", "target"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.templates": {
+ "name": "templates",
+ "schema": "",
+ "columns": {
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "id": {
+ "name": "id",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel": {
+ "name": "channel",
+ "type": "channel",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "topics": {
+ "name": "topics",
+ "type": "text[]",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "content": {
+ "name": "content",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ai_prompts": {
+ "name": "ai_prompts",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "templates_project_id_projects_id_fk": {
+ "name": "templates_project_id_projects_id_fk",
+ "tableFrom": "templates",
+ "tableTo": "projects",
+ "columnsFrom": ["project_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "templates_project_id_id_pk": {
+ "name": "templates_project_id_id_pk",
+ "columns": ["project_id", "id"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_channel_preferences": {
+ "name": "user_channel_preferences",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel": {
+ "name": "channel",
+ "type": "channel",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "user_channel_preferences_user_id_users_id_fk": {
+ "name": "user_channel_preferences_user_id_users_id_fk",
+ "tableFrom": "user_channel_preferences",
+ "tableTo": "users",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "user_channel_preferences_user_id_channel_pk": {
+ "name": "user_channel_preferences_user_id_channel_pk",
+ "columns": ["user_id", "channel"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_contacts": {
+ "name": "user_contacts",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "channel": {
+ "name": "channel",
+ "type": "channel",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target": {
+ "name": "target",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "label": {
+ "name": "label",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_primary": {
+ "name": "is_primary",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "user_contacts_user_id_users_id_fk": {
+ "name": "user_contacts_user_id_users_id_fk",
+ "tableFrom": "user_contacts",
+ "tableTo": "users",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_contacts_user_id_channel_target_unique": {
+ "name": "user_contacts_user_id_channel_target_unique",
+ "nullsNotDistinct": false,
+ "columns": ["user_id", "channel", "target"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_segments": {
+ "name": "user_segments",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "segment": {
+ "name": "segment",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "segment_idx": {
+ "name": "segment_idx",
+ "columns": [
+ {
+ "expression": "segment",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "user_segments_user_id_users_id_fk": {
+ "name": "user_segments_user_id_users_id_fk",
+ "tableFrom": "user_segments",
+ "tableTo": "users",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_segments_user_id_segment_unique": {
+ "name": "user_segments_user_id_segment_unique",
+ "nullsNotDistinct": false,
+ "columns": ["user_id", "segment"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_topic_preferences": {
+ "name": "user_topic_preferences",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "topic": {
+ "name": "topic",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "user_topic_preferences_user_id_users_id_fk": {
+ "name": "user_topic_preferences_user_id_users_id_fk",
+ "tableFrom": "user_topic_preferences",
+ "tableTo": "users",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_topic_preferences_user_id_topic_unique": {
+ "name": "user_topic_preferences_user_id_topic_unique",
+ "nullsNotDistinct": false,
+ "columns": ["user_id", "topic"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.users": {
+ "name": "users",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "external_id": {
+ "name": "external_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "attributes": {
+ "name": "attributes",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "users_project_id_external_id_unique": {
+ "name": "users_project_id_external_id_unique",
+ "nullsNotDistinct": false,
+ "columns": ["project_id", "external_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.workflow_definitions": {
+ "name": "workflow_definitions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "steps": {
+ "name": "steps",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "workflow_definitions_project_id_name_unique": {
+ "name": "workflow_definitions_project_id_name_unique",
+ "nullsNotDistinct": false,
+ "columns": ["project_id", "name"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.workflow_instances": {
+ "name": "workflow_instances",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "workflow_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "input": {
+ "name": "input",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{}'::jsonb"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "workflow_name_idx": {
+ "name": "workflow_name_idx",
+ "columns": [
+ {
+ "expression": "name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.workflow_steps": {
+ "name": "workflow_steps",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "instance_id": {
+ "name": "instance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "step_index": {
+ "name": "step_index",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action": {
+ "name": "action",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "output": {
+ "name": "output",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "error": {
+ "name": "error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "workflow_steps_instance_id_workflow_instances_id_fk": {
+ "name": "workflow_steps_instance_id_workflow_instances_id_fk",
+ "tableFrom": "workflow_steps",
+ "tableTo": "workflow_instances",
+ "columnsFrom": ["instance_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "workflow_steps_instance_id_step_index_unique": {
+ "name": "workflow_steps_instance_id_step_index_unique",
+ "nullsNotDistinct": false,
+ "columns": ["instance_id", "step_index"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.workflow_waiters": {
+ "name": "workflow_waiters",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "instance_id": {
+ "name": "instance_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_name": {
+ "name": "event_name",
+ "type": "varchar",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "match_criteria": {
+ "name": "match_criteria",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "waiter_event_idx": {
+ "name": "waiter_event_idx",
+ "columns": [
+ {
+ "expression": "event_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "waiter_instance_idx": {
+ "name": "waiter_instance_idx",
+ "columns": [
+ {
+ "expression": "instance_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "waiter_comp_idx": {
+ "name": "waiter_comp_idx",
+ "columns": [
+ {
+ "expression": "event_name",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "project_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "expires_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "waiter_match_idx": {
+ "name": "waiter_match_idx",
+ "columns": [
+ {
+ "expression": "match_criteria",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "gin",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "workflow_waiters_instance_id_workflow_instances_id_fk": {
+ "name": "workflow_waiters_instance_id_workflow_instances_id_fk",
+ "tableFrom": "workflow_waiters",
+ "tableTo": "workflow_instances",
+ "columnsFrom": ["instance_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {
+ "public.api_key_role": {
+ "name": "api_key_role",
+ "schema": "public",
+ "values": ["admin", "read_only"]
+ },
+ "public.channel": {
+ "name": "channel",
+ "schema": "public",
+ "values": ["email", "sms", "push", "webhook", "in-app"]
+ },
+ "public.workflow_status": {
+ "name": "workflow_status",
+ "schema": "public",
+ "values": ["pending", "running", "completed", "failed"]
+ }
+ },
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json
new file mode 100644
index 0000000..6b688cb
--- /dev/null
+++ b/drizzle/meta/_journal.json
@@ -0,0 +1,20 @@
+{
+ "version": "7",
+ "dialect": "postgresql",
+ "entries": [
+ {
+ "idx": 0,
+ "version": "7",
+ "when": 1786087159272,
+ "tag": "0000_spotty_jack_flag",
+ "breakpoints": true
+ },
+ {
+ "idx": 1,
+ "version": "7",
+ "when": 1786643990078,
+ "tag": "0001_stale_shotgun",
+ "breakpoints": true
+ }
+ ]
+}
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 0000000..5f14e94
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,45 @@
+import tseslint from "@typescript-eslint/eslint-plugin";
+import tsParser from "@typescript-eslint/parser";
+
+/** @type {import('eslint').Linter.FlatConfig[]} */
+export default [
+ {
+ ignores: [
+ "**/dist/**",
+ "**/node_modules/**",
+ "**/*.js",
+ "**/*.cjs",
+ "**/*.mjs",
+ "**/tsup.config.ts",
+ "**/vitest.config.ts"
+ ]
+ },
+ {
+ files: ["**/*.ts", "**/*.tsx"],
+ languageOptions: {
+ parser: tsParser,
+ parserOptions: {
+ project: true,
+ tsconfigRootDir: import.meta.dirname
+ }
+ },
+ plugins: {
+ "@typescript-eslint": tseslint
+ },
+ rules: {
+ ...tseslint.configs.recommended.rules,
+ "@typescript-eslint/consistent-type-definitions": ["error", "interface"],
+ "@typescript-eslint/consistent-type-imports": [
+ "error",
+ { "prefer": "type-imports" }
+ ],
+ "@typescript-eslint/no-explicit-any": "warn",
+ "@typescript-eslint/no-floating-promises": "error",
+ "@typescript-eslint/no-misused-promises": "error",
+ "@typescript-eslint/no-unused-vars": [
+ "error",
+ { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_", "caughtErrorsIgnorePattern": "^_" }
+ ]
+ }
+ }
+];
diff --git a/examples/basic-usage/index.js b/examples/basic-usage/index.js
new file mode 100644
index 0000000..c7c6921
--- /dev/null
+++ b/examples/basic-usage/index.js
@@ -0,0 +1,87 @@
+/**
+ * Example project demonstrating how to use Notifkit via its HTTP API โ no SDK,
+ * just fetch. Any language that can POST JSON works the same way.
+ *
+ * Before running this, start a Notifkit server. The custom-server example is
+ * one:
+ * $ cd ../custom-server && node index.js
+ *
+ * To run this example:
+ * $ ADMIN_API_KEY=my_super_secret_admin_key npm start
+ */
+
+const API_BASE = process.env.NOTIFKIT_API_URL || 'http://localhost:3000/v1';
+const API_KEY = process.env.ADMIN_API_KEY || 'my_super_secret_admin_key';
+
+// Every /v1 route is authenticated. Only /health, /live, /ready and /metrics are not.
+const headers = {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${API_KEY}`,
+};
+
+async function main() {
+ console.log('๐ Starting Notifkit example...\n');
+
+ try {
+ // 1. Sync Templates (Configure how notifications should look)
+ console.log('1๏ธโฃ Syncing email template...');
+ const templateRes = await fetch(`${API_BASE}/templates`, {
+ method: 'PUT',
+ headers,
+ body: JSON.stringify({
+ templates: [{
+ id: 'welcome_email',
+ channel: 'email',
+ // The per-channel fields live under `content`.
+ content: {
+ subject: 'Welcome to Notifkit, {{name}}!',
+ html: 'Hello {{name}}Thanks for signing up! ',
+ text: 'Hello {{name}}! Thanks for signing up!'
+ }
+ }]
+ })
+ });
+ console.log('Template Response:', await templateRes.json());
+ console.log();
+
+ // 2. Create a User (Register a recipient)
+ console.log('2๏ธโฃ Registering a user...');
+ const userRes = await fetch(`${API_BASE}/users`, {
+ method: 'POST',
+ headers,
+ body: JSON.stringify({
+ id: 'user_123',
+ email: 'test@example.com',
+ preferences: {
+ channels: { email: true }
+ }
+ })
+ });
+ console.log('User Response:', await userRes.json());
+ console.log();
+
+ // 3. Send a Notification
+ console.log('3๏ธโฃ Sending notification...');
+ const notifyRes = await fetch(`${API_BASE}/notify`, {
+ method: 'POST',
+ headers,
+ body: JSON.stringify({
+ user: 'user_123',
+ template: 'welcome_email',
+ channels: ['email'],
+ data: {
+ name: 'Alice'
+ }
+ })
+ });
+ // 202 Accepted โ queued, not delivered. Track it with
+ // GET /v1/notifications/{taskId}.
+ console.log('Notify Response:', await notifyRes.json());
+ console.log('\nโ
Example completed successfully!');
+
+ } catch (err) {
+ console.error('โ Error executing example:', err.message);
+ }
+}
+
+main();
diff --git a/examples/basic-usage/package.json b/examples/basic-usage/package.json
new file mode 100644
index 0000000..d61a211
--- /dev/null
+++ b/examples/basic-usage/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "notifkit-example",
+ "version": "1.0.0",
+ "type": "module",
+ "description": "Example Node.js project demonstrating Notifkit API usage",
+ "scripts": {
+ "start": "node index.js"
+ }
+}
diff --git a/examples/custom-server/README.md b/examples/custom-server/README.md
new file mode 100644
index 0000000..5d800e4
--- /dev/null
+++ b/examples/custom-server/README.md
@@ -0,0 +1,19 @@
+# Custom Server Example
+
+This example demonstrates how to bootstrap the `NotifkitServer` orchestrator with custom notification provider transports, custom concurrency options, and graceful process shutdown handlers.
+
+## Prerequisites
+
+- Node.js >= 22
+- Running PostgreSQL and Redis instances (or environment variables `DATABASE_URL` and `REDIS_URL`)
+
+## How to Run
+
+```bash
+# Set environment variables (optional if running locally with defaults)
+export DATABASE_URL="postgresql://platform:platform@localhost:5432/notifkit"
+export REDIS_URL="redis://localhost:6379"
+
+# Run the server example
+node index.js
+```
diff --git a/examples/custom-server/index.js b/examples/custom-server/index.js
new file mode 100644
index 0000000..48cceba
--- /dev/null
+++ b/examples/custom-server/index.js
@@ -0,0 +1,59 @@
+import { NotifkitServer } from 'notifkit';
+
+/**
+ * Example Custom Email Transport Provider
+ */
+class CustomEmailTransport {
+ constructor() {
+ this.channel = 'email';
+ }
+
+ async send(task) {
+ console.log(`[CustomEmailTransport] Sending notification to ${task.recipient.email}...`);
+
+ // Simulate async network request to email gateway
+ await new Promise((resolve) => setTimeout(resolve, 100));
+
+ return {
+ success: true,
+ providerMessageId: `custom-msg-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`,
+ };
+ }
+}
+
+async function main() {
+ console.log('๐ Starting custom Notifkit Server example...');
+
+ const server = new NotifkitServer({
+ services: ['all'],
+ port: Number(process.env.PORT || 4000),
+ logLevel: process.env.LOG_LEVEL || 'info',
+ redisUrl: process.env.REDIS_URL,
+ databaseUrl: process.env.DATABASE_URL,
+ workerConcurrency: 50,
+ providers: [
+ new CustomEmailTransport(),
+ ],
+ });
+
+ try {
+ await server.start();
+ console.log('โ
Notifkit Server started successfully on port 4000.');
+
+ // Graceful shutdown handling
+ const shutdown = async (signal) => {
+ console.log(`\n๐ Received ${signal}, stopping Notifkit Server...`);
+ await server.stop();
+ console.log('๐ Server stopped cleanly.');
+ process.exit(0);
+ };
+
+ process.once('SIGINT', () => shutdown('SIGINT'));
+ process.once('SIGTERM', () => shutdown('SIGTERM'));
+ } catch (error) {
+ console.error('โ Failed to start Notifkit Server:', error);
+ process.exit(1);
+ }
+}
+
+main();
diff --git a/examples/load-test/README.md b/examples/load-test/README.md
new file mode 100644
index 0000000..f78ca71
--- /dev/null
+++ b/examples/load-test/README.md
@@ -0,0 +1,14 @@
+# High-Throughput Load Test Example
+
+This example measures request throughput and latency for the Notifkit REST API ingestion pipeline.
+
+## How to Run
+
+```bash
+export NOTIFKIT_API_URL="http://localhost:4000"
+export ADMIN_API_KEY="your_admin_api_key"
+export COUNT=1000
+export CONCURRENCY=100
+
+node index.js
+```
diff --git a/examples/load-test/index.js b/examples/load-test/index.js
new file mode 100644
index 0000000..96ab156
--- /dev/null
+++ b/examples/load-test/index.js
@@ -0,0 +1,61 @@
+import { NotifkitClient } from 'notifkit';
+
+/**
+ * Load testing example for measuring Notifkit pipeline throughput.
+ */
+async function runLoadTest() {
+ const baseUrl = process.env.NOTIFKIT_API_URL || 'http://localhost:4000';
+ const apiKey = process.env.ADMIN_API_KEY || 'your_admin_api_key';
+ const totalNotifications = Number(process.env.COUNT || 500);
+ const concurrency = Number(process.env.CONCURRENCY || 50);
+
+ const client = new NotifkitClient({ baseUrl, apiKey });
+
+ console.log(`โก Starting load test: ${totalNotifications} notifications (Concurrency: ${concurrency})...`);
+ const startTime = Date.now();
+
+ let completed = 0;
+ let failed = 0;
+
+ const queue = Array.from({ length: totalNotifications }, (_, i) => ({
+ template: 'benchmark-template',
+ channels: ['email'],
+ user: {
+ id: `bench-user-${i % 100}`,
+ email: `bench-user-${i % 100}@example.com`,
+ },
+ data: { index: i },
+ }));
+
+ async function worker() {
+ while (queue.length > 0) {
+ const item = queue.pop();
+ if (!item) break;
+
+ try {
+ await client.notify(item);
+ completed++;
+ } catch (err) {
+ failed++;
+ }
+ }
+ }
+
+ const workers = Array.from({ length: concurrency }, () => worker());
+ await Promise.all(workers);
+
+ const elapsedMs = Date.now() - startTime;
+ const throughputSec = (completed / (elapsedMs / 1000)).toFixed(2);
+
+ console.log(`\n๐ Load Test Results:`);
+ console.log(` - Total Processed: ${completed + failed}`);
+ console.log(` - Successful: ${completed}`);
+ console.log(` - Failed: ${failed}`);
+ console.log(` - Elapsed Time: ${elapsedMs} ms`);
+ console.log(` - Throughput: ${throughputSec} req/sec`);
+}
+
+runLoadTest().catch((err) => {
+ console.error('โ Error executing load test:', err);
+ process.exit(1);
+});
diff --git a/examples/scheduled-notifications/README.md b/examples/scheduled-notifications/README.md
new file mode 100644
index 0000000..7111c4c
--- /dev/null
+++ b/examples/scheduled-notifications/README.md
@@ -0,0 +1,12 @@
+# Scheduled Notifications & Quiet Hours Example
+
+This example demonstrates how to schedule future notification delivery (`sendAt`) and enforce recipient timezone-aware quiet hours deferrals.
+
+## How to Run
+
+```bash
+export NOTIFKIT_API_URL="http://localhost:4000"
+export ADMIN_API_KEY="your_admin_api_key"
+
+node index.js
+```
diff --git a/examples/scheduled-notifications/index.js b/examples/scheduled-notifications/index.js
new file mode 100644
index 0000000..56518af
--- /dev/null
+++ b/examples/scheduled-notifications/index.js
@@ -0,0 +1,53 @@
+import { NotifkitClient } from 'notifkit';
+
+/**
+ * Example demonstrating scheduled and quiet-hours aware notification delivery.
+ */
+async function runScheduledNotificationsExample() {
+ const client = new NotifkitClient({
+ baseUrl: process.env.NOTIFKIT_API_URL || 'http://localhost:4000',
+ apiKey: process.env.ADMIN_API_KEY || 'your_admin_api_key',
+ });
+
+ console.log('๐ Syncing template...');
+ await client.syncTemplates({
+ templates: [
+ {
+ id: 'scheduled-reminder',
+ channel: 'email',
+ content: {
+ subject: 'Scheduled Meeting Reminder',
+ html: 'Meeting ReminderHi {{name}}, your meeting starts in 15 minutes. ',
+ text: 'Hi {{name}}, your meeting starts in 15 minutes.',
+ },
+ },
+ ],
+ });
+
+ // Schedule notification for 10 minutes in the future
+ const sendAt = new Date(Date.now() + 10 * 60 * 1000).toISOString();
+
+ console.log(`โฐ Dispatching scheduled notification for ${sendAt}...`);
+ const response = await client.notify({
+ template: 'scheduled-reminder',
+ channels: ['email'],
+ user: {
+ id: 'user-2002',
+ email: 'alex@example.com',
+ timezone: 'America/New_York',
+ // Quiet hours live under `preferences`, and are a list of UTC windows.
+ preferences: {
+ quietHours: [{ start: '22:00', end: '08:00' }],
+ },
+ },
+ data: { name: 'Alex' },
+ sendAt,
+ });
+
+ console.log('โ
Scheduled notification response:', response);
+}
+
+runScheduledNotificationsExample().catch((err) => {
+ console.error('โ Error executing scheduled notification example:', err);
+ process.exit(1);
+});
diff --git a/examples/workflows/README.md b/examples/workflows/README.md
new file mode 100644
index 0000000..82fa8a0
--- /dev/null
+++ b/examples/workflows/README.md
@@ -0,0 +1,18 @@
+# Multi-step Workflows Example
+
+This example demonstrates how to define, sync, and trigger multi-step drip notification workflows using the `NotifkitClient` SDK.
+
+## Workflow Structure
+
+1. **Step 1**: Dispatches immediate welcome email (`onboarding-welcome`).
+2. **Step 2**: Pauses execution using `wait` (`1h`).
+3. **Step 3**: Dispatches follow-up email (`onboarding-followup`).
+
+## How to Run
+
+```bash
+export NOTIFKIT_API_URL="http://localhost:4000"
+export ADMIN_API_KEY="your_admin_api_key"
+
+node index.js
+```
diff --git a/examples/workflows/index.js b/examples/workflows/index.js
new file mode 100644
index 0000000..211c042
--- /dev/null
+++ b/examples/workflows/index.js
@@ -0,0 +1,84 @@
+import { NotifkitClient } from 'notifkit';
+
+/**
+ * Example demonstrating multi-step notification workflows (Notify -> Wait -> Notify)
+ */
+async function runWorkflowExample() {
+ const client = new NotifkitClient({
+ baseUrl: process.env.NOTIFKIT_API_URL || 'http://localhost:4000',
+ apiKey: process.env.ADMIN_API_KEY || 'your_admin_api_key',
+ });
+
+ console.log('๐ Syncing onboarding template...');
+ await client.syncTemplates({
+ templates: [
+ {
+ id: 'onboarding-welcome',
+ channel: 'email',
+ content: {
+ subject: 'Welcome to our Platform!',
+ html: 'Hello {{name}}!Thank you for signing up. ',
+ text: 'Hello {{name}}! Thank you for signing up.',
+ },
+ },
+ {
+ id: 'onboarding-followup',
+ channel: 'email',
+ content: {
+ subject: 'Getting Started Tips',
+ html: 'Tips for {{name}}Check out our documentation to get started. ',
+ text: 'Tips for {{name}}. Check out our documentation to get started.',
+ },
+ },
+ ],
+ });
+ console.log('โ
Templates synced successfully.');
+
+ console.log('๐ Registering multi-step workflow definition...');
+ // No step names a recipient: each one inherits the user the instance was
+ // triggered with, so the same definition serves every subscriber.
+ const workflow = await client.createWorkflow({
+ name: 'user-onboarding-drip',
+ steps: [
+ {
+ action: 'notify',
+ payload: {
+ template: 'onboarding-welcome',
+ channels: ['email'],
+ data: { name: 'Jane Doe' },
+ },
+ },
+ {
+ action: 'wait',
+ duration: '1h', // Wait 1 hour before next step
+ },
+ {
+ action: 'notify',
+ payload: {
+ template: 'onboarding-followup',
+ channels: ['email'],
+ data: { name: 'Jane Doe' },
+ },
+ },
+ ],
+ });
+
+ console.log(`โ
Workflow registered: ${workflow.name}`);
+
+ console.log('๐ Registering the recipient...');
+ await client.addUser({ id: 'user-1001', email: 'user@example.com' });
+
+ console.log('๐ Triggering workflow execution for recipient...');
+ // input.user.id is who every notify step in this instance sends to.
+ const execution = await client.triggerWorkflow({
+ name: 'user-onboarding-drip',
+ input: { user: { id: 'user-1001' } },
+ });
+
+ console.log('๐ Workflow triggered. Instance ID:', execution.instanceId);
+}
+
+runWorkflowExample().catch((err) => {
+ console.error('โ Error executing workflow example:', err);
+ process.exit(1);
+});
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..b3d96d7
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,12930 @@
+{
+ "name": "notifkit",
+ "version": "0.0.1",
+ "lockfileVersion": 3,
+ "requires": true,
+ "dev": true,
+ "packages": {
+ "": {
+ "name": "notifkit",
+ "version": "0.0.1",
+ "license": "MIT",
+ "workspaces": [
+ "packages/*"
+ ],
+ "dependencies": {
+ "ai": "^7.0.37",
+ "dotenv": "^16.4.5",
+ "drizzle-orm": "^0.45.2",
+ "drizzle-zod": "^0.8.3",
+ "ioredis": "^5.4.1",
+ "pg": "^8.22.0",
+ "pino": "^9.2.0",
+ "pino-pretty": "^11.0.0",
+ "postgres": "^3.4.4",
+ "prom-client": "^15.1.3",
+ "zod": "^3.23.8"
+ },
+ "devDependencies": {
+ "@commitlint/cli": "^19.3.0",
+ "@commitlint/config-conventional": "^19.2.2",
+ "@testcontainers/postgresql": "^12.0.4",
+ "@testcontainers/redis": "^12.0.4",
+ "@types/node": "^22.0.0",
+ "@types/supertest": "^7.2.1",
+ "@typescript-eslint/eslint-plugin": "^8.0.0",
+ "@typescript-eslint/parser": "^8.0.0",
+ "@vitest/coverage-v8": "^2.0.0",
+ "drizzle-kit": "^0.31.10",
+ "eslint": "^9.0.0",
+ "husky": "^9.0.11",
+ "lint-staged": "^15.2.5",
+ "prettier": "^3.3.0",
+ "supertest": "^7.2.2",
+ "testcontainers": "^12.0.4",
+ "tsdown": "^0.22.14",
+ "typescript": "^5.8.3",
+ "vitest": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=22.0.0",
+ "npm": ">=10.0.0"
+ }
+ },
+ "node_modules/@ai-sdk/gateway": {
+ "version": "4.0.28",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.28.tgz",
+ "integrity": "sha512-ee9TsNO3mkgHDWmTmJ8Fvltr6PFh52zhrV2+FaKJY3F3iu7kWZi5tCRJCR1HVhhlpj6lHniUb28nLQdPHkA/1Q==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@ai-sdk/provider": "4.0.3",
+ "@ai-sdk/provider-utils": "5.0.12",
+ "@vercel/oidc": "3.2.0"
+ },
+ "engines": {
+ "node": ">=22"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.76 || ^4.1.8"
+ }
+ },
+ "node_modules/@ai-sdk/provider": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-4.0.3.tgz",
+ "integrity": "sha512-e0CpNWJUY7OxAFAnCZkw+ri9QOHWwTs1tXP42782KFGCU07qt8NiXCrCVowyCB5dP2r5/Uls+g2oPd8kOJn9dw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "json-schema": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=22"
+ }
+ },
+ "node_modules/@ai-sdk/provider-utils": {
+ "version": "5.0.12",
+ "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.12.tgz",
+ "integrity": "sha512-bbhlOgHeYwrIGheLkM6fhS8hVger8uFPmcOLg+kxc9EFh7y30XYorWhthlYAgpadO3SJhFZrIcEknN7qEqEVvA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@ai-sdk/provider": "4.0.3",
+ "@standard-schema/spec": "^1.1.0",
+ "@workflow/serde": "4.1.0",
+ "eventsource-parser": "^3.0.8"
+ },
+ "engines": {
+ "node": ">=22"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.76 || ^4.1.8"
+ }
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
+ "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
+ "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
+ "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.7"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
+ "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@balena/dockerignore": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz",
+ "integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/@bcoe/v8-coverage": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
+ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@cfworker/json-schema": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/@cfworker/json-schema/-/json-schema-4.1.1.tgz",
+ "integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==",
+ "license": "MIT"
+ },
+ "node_modules/@commitlint/cli": {
+ "version": "19.8.1",
+ "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.8.1.tgz",
+ "integrity": "sha512-LXUdNIkspyxrlV6VDHWBmCZRtkEVRpBKxi2Gtw3J54cGWhLCTouVD/Q6ZSaSvd2YaDObWK8mDjrz3TIKtaQMAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/format": "^19.8.1",
+ "@commitlint/lint": "^19.8.1",
+ "@commitlint/load": "^19.8.1",
+ "@commitlint/read": "^19.8.1",
+ "@commitlint/types": "^19.8.1",
+ "tinyexec": "^1.0.0",
+ "yargs": "^17.0.0"
+ },
+ "bin": {
+ "commitlint": "cli.js"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/config-conventional": {
+ "version": "19.8.1",
+ "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-19.8.1.tgz",
+ "integrity": "sha512-/AZHJL6F6B/G959CsMAzrPKKZjeEiAVifRyEwXxcT6qtqbPwGw+iQxmNS+Bu+i09OCtdNRW6pNpBvgPrtMr9EQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^19.8.1",
+ "conventional-changelog-conventionalcommits": "^7.0.2"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/config-validator": {
+ "version": "19.8.1",
+ "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-19.8.1.tgz",
+ "integrity": "sha512-0jvJ4u+eqGPBIzzSdqKNX1rvdbSU1lPNYlfQQRIFnBgLy26BtC0cFnr7c/AyuzExMxWsMOte6MkTi9I3SQ3iGQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^19.8.1",
+ "ajv": "^8.11.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/ensure": {
+ "version": "19.8.1",
+ "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-19.8.1.tgz",
+ "integrity": "sha512-mXDnlJdvDzSObafjYrOSvZBwkD01cqB4gbnnFuVyNpGUM5ijwU/r/6uqUmBXAAOKRfyEjpkGVZxaDsCVnHAgyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^19.8.1",
+ "lodash.camelcase": "^4.3.0",
+ "lodash.kebabcase": "^4.1.1",
+ "lodash.snakecase": "^4.1.1",
+ "lodash.startcase": "^4.4.0",
+ "lodash.upperfirst": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/execute-rule": {
+ "version": "19.8.1",
+ "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-19.8.1.tgz",
+ "integrity": "sha512-YfJyIqIKWI64Mgvn/sE7FXvVMQER/Cd+s3hZke6cI1xgNT/f6ZAz5heND0QtffH+KbcqAwXDEE1/5niYayYaQA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/format": {
+ "version": "19.8.1",
+ "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-19.8.1.tgz",
+ "integrity": "sha512-kSJj34Rp10ItP+Eh9oCItiuN/HwGQMXBnIRk69jdOwEW9llW9FlyqcWYbHPSGofmjsqeoxa38UaEA5tsbm2JWw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^19.8.1",
+ "chalk": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/is-ignored": {
+ "version": "19.8.1",
+ "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-19.8.1.tgz",
+ "integrity": "sha512-AceOhEhekBUQ5dzrVhDDsbMaY5LqtN8s1mqSnT2Kz1ERvVZkNihrs3Sfk1Je/rxRNbXYFzKZSHaPsEJJDJV8dg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^19.8.1",
+ "semver": "^7.6.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/lint": {
+ "version": "19.8.1",
+ "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-19.8.1.tgz",
+ "integrity": "sha512-52PFbsl+1EvMuokZXLRlOsdcLHf10isTPlWwoY1FQIidTsTvjKXVXYb7AvtpWkDzRO2ZsqIgPK7bI98x8LRUEw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/is-ignored": "^19.8.1",
+ "@commitlint/parse": "^19.8.1",
+ "@commitlint/rules": "^19.8.1",
+ "@commitlint/types": "^19.8.1"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/load": {
+ "version": "19.8.1",
+ "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-19.8.1.tgz",
+ "integrity": "sha512-9V99EKG3u7z+FEoe4ikgq7YGRCSukAcvmKQuTtUyiYPnOd9a2/H9Ak1J9nJA1HChRQp9OA/sIKPugGS+FK/k1A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/config-validator": "^19.8.1",
+ "@commitlint/execute-rule": "^19.8.1",
+ "@commitlint/resolve-extends": "^19.8.1",
+ "@commitlint/types": "^19.8.1",
+ "chalk": "^5.3.0",
+ "cosmiconfig": "^9.0.0",
+ "cosmiconfig-typescript-loader": "^6.1.0",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.merge": "^4.6.2",
+ "lodash.uniq": "^4.5.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/message": {
+ "version": "19.8.1",
+ "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-19.8.1.tgz",
+ "integrity": "sha512-+PMLQvjRXiU+Ae0Wc+p99EoGEutzSXFVwQfa3jRNUZLNW5odZAyseb92OSBTKCu+9gGZiJASt76Cj3dLTtcTdg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/parse": {
+ "version": "19.8.1",
+ "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-19.8.1.tgz",
+ "integrity": "sha512-mmAHYcMBmAgJDKWdkjIGq50X4yB0pSGpxyOODwYmoexxxiUCy5JJT99t1+PEMK7KtsCtzuWYIAXYAiKR+k+/Jw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/types": "^19.8.1",
+ "conventional-changelog-angular": "^7.0.0",
+ "conventional-commits-parser": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/read": {
+ "version": "19.8.1",
+ "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-19.8.1.tgz",
+ "integrity": "sha512-03Jbjb1MqluaVXKHKRuGhcKWtSgh3Jizqy2lJCRbRrnWpcM06MYm8th59Xcns8EqBYvo0Xqb+2DoZFlga97uXQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/top-level": "^19.8.1",
+ "@commitlint/types": "^19.8.1",
+ "git-raw-commits": "^4.0.0",
+ "minimist": "^1.2.8",
+ "tinyexec": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/resolve-extends": {
+ "version": "19.8.1",
+ "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-19.8.1.tgz",
+ "integrity": "sha512-GM0mAhFk49I+T/5UCYns5ayGStkTt4XFFrjjf0L4S26xoMTSkdCf9ZRO8en1kuopC4isDFuEm7ZOm/WRVeElVg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/config-validator": "^19.8.1",
+ "@commitlint/types": "^19.8.1",
+ "global-directory": "^4.0.1",
+ "import-meta-resolve": "^4.0.0",
+ "lodash.mergewith": "^4.6.2",
+ "resolve-from": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/rules": {
+ "version": "19.8.1",
+ "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-19.8.1.tgz",
+ "integrity": "sha512-Hnlhd9DyvGiGwjfjfToMi1dsnw1EXKGJNLTcsuGORHz6SS9swRgkBsou33MQ2n51/boIDrbsg4tIBbRpEWK2kw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@commitlint/ensure": "^19.8.1",
+ "@commitlint/message": "^19.8.1",
+ "@commitlint/to-lines": "^19.8.1",
+ "@commitlint/types": "^19.8.1"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/to-lines": {
+ "version": "19.8.1",
+ "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-19.8.1.tgz",
+ "integrity": "sha512-98Mm5inzbWTKuZQr2aW4SReY6WUukdWXuZhrqf1QdKPZBCCsXuG87c+iP0bwtD6DBnmVVQjgp4whoHRVixyPBg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/top-level": {
+ "version": "19.8.1",
+ "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-19.8.1.tgz",
+ "integrity": "sha512-Ph8IN1IOHPSDhURCSXBz44+CIu+60duFwRsg6HqaISFHQHbmBtxVw4ZrFNIYUzEP7WwrNPxa2/5qJ//NK1FGcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "find-up": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@commitlint/types": {
+ "version": "19.8.1",
+ "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-19.8.1.tgz",
+ "integrity": "sha512-/yCrWGCoA1SVKOks25EGadP9Pnj0oAIHGpl2wH2M2Y46dPM2ueb8wyCVOD7O3WCTkaJ0IkKvzhl1JY7+uCT2Dw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/conventional-commits-parser": "^5.0.0",
+ "chalk": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=v18"
+ }
+ },
+ "node_modules/@drizzle-team/brocli": {
+ "version": "0.10.2",
+ "resolved": "https://registry.npmjs.org/@drizzle-team/brocli/-/brocli-0.10.2.tgz",
+ "integrity": "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/@emnapi/core": {
+ "version": "2.0.0-alpha.3",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-2.0.0-alpha.3.tgz",
+ "integrity": "sha512-AZypUeJ/yByuxyS7BlSNRDOMLMlROYtjYdIAuBmJssVz1UJDSeYxLrdizhXCFYhedC5bqd/ASy8EuNXbVVXp9g==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "2.0.1",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "2.0.0-alpha.3",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-2.0.0-alpha.3.tgz",
+ "integrity": "sha512-hFPAhMUjJD9BSyCANEISPOogeXC9Zo9ZQl7L6vKnaVsMkCtzznaW/naYypeyl0Gv5rYfWYsZbpixTMpjDJzQeA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-2.0.1.tgz",
+ "integrity": "sha512-9DsSk+o5NBX0CCJT8s0EROGSGxjR/tKu6aBTaVyq+SjAEQH4XcdcRxPBRzsBLizTTJ49MJjF+jgu3qnO9GLQcQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz",
+ "integrity": "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==",
+ "deprecated": "Merged into tsx: https://tsx.hirok.io",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "~0.18.20",
+ "source-map-support": "^0.5.21"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
+ "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz",
+ "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz",
+ "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz",
+ "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz",
+ "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz",
+ "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz",
+ "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz",
+ "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz",
+ "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ia32": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz",
+ "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-loong64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz",
+ "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz",
+ "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz",
+ "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz",
+ "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-s390x": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz",
+ "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
+ "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz",
+ "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz",
+ "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/sunos-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz",
+ "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-arm64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz",
+ "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-ia32": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz",
+ "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz",
+ "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild-kit/core-utils/node_modules/esbuild": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
+ "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/android-arm": "0.18.20",
+ "@esbuild/android-arm64": "0.18.20",
+ "@esbuild/android-x64": "0.18.20",
+ "@esbuild/darwin-arm64": "0.18.20",
+ "@esbuild/darwin-x64": "0.18.20",
+ "@esbuild/freebsd-arm64": "0.18.20",
+ "@esbuild/freebsd-x64": "0.18.20",
+ "@esbuild/linux-arm": "0.18.20",
+ "@esbuild/linux-arm64": "0.18.20",
+ "@esbuild/linux-ia32": "0.18.20",
+ "@esbuild/linux-loong64": "0.18.20",
+ "@esbuild/linux-mips64el": "0.18.20",
+ "@esbuild/linux-ppc64": "0.18.20",
+ "@esbuild/linux-riscv64": "0.18.20",
+ "@esbuild/linux-s390x": "0.18.20",
+ "@esbuild/linux-x64": "0.18.20",
+ "@esbuild/netbsd-x64": "0.18.20",
+ "@esbuild/openbsd-x64": "0.18.20",
+ "@esbuild/sunos-x64": "0.18.20",
+ "@esbuild/win32-arm64": "0.18.20",
+ "@esbuild/win32-ia32": "0.18.20",
+ "@esbuild/win32-x64": "0.18.20"
+ }
+ },
+ "node_modules/@esbuild-kit/esm-loader": {
+ "version": "2.6.5",
+ "resolved": "https://registry.npmjs.org/@esbuild-kit/esm-loader/-/esm-loader-2.6.5.tgz",
+ "integrity": "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==",
+ "deprecated": "Merged into tsx: https://tsx.hirok.io",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@esbuild-kit/core-utils": "^3.3.2",
+ "get-tsconfig": "^4.7.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+ "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.21.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz",
+ "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.7",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.5"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-array/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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@eslint/config-array/node_modules/brace-expansion": {
+ "version": "1.1.16",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz",
+ "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/@eslint/config-array/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
+ "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
+ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz",
+ "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.14.0",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.3.0",
+ "minimatch": "^3.1.5",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/ajv": {
+ "version": "6.15.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz",
+ "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/@eslint/eslintrc/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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
+ "version": "1.1.16",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz",
+ "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@eslint/eslintrc/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.39.5",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz",
+ "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
+ "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@fastify/busboy": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz",
+ "integrity": "sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==",
+ "license": "MIT"
+ },
+ "node_modules/@firebase/app-check-interop-types": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.2.tgz",
+ "integrity": "sha512-LMs47Vinv2HBMZi49C09dJxp0QT5LwDzFaVGf/+ITHe3BlIhUiLNttkATSXplc89A2lAaeTqjgqVkiRfUGyQiQ==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@firebase/app-types": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.2.tgz",
+ "integrity": "sha512-oMEZ1TDlBz479lmABwWsWjzHwheQKiAgnuKxE0pz0IXCVx7/rtlkx1fQ6GfgK24WCrxDKMplZrT50Kh04iMbXQ==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@firebase/auth-interop-types": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.3.tgz",
+ "integrity": "sha512-Fc9wuJGgxoxQeavybiuwgyi+0rssr76b+nHpj+eGhXFYAdudMWyfBHvFL/I5fEHniUM/UQdFzi9VXJK2iZF7FQ==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@firebase/component": {
+ "version": "0.6.9",
+ "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.9.tgz",
+ "integrity": "sha512-gm8EUEJE/fEac86AvHn8Z/QW8BvR56TBw3hMW0O838J/1mThYQXAIQBgUv75EqlCZfdawpWLrKt1uXvp9ciK3Q==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/util": "1.10.0",
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@firebase/database": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.8.tgz",
+ "integrity": "sha512-dzXALZeBI1U5TXt6619cv0+tgEhJiwlUtQ55WNZY7vGAjv7Q1QioV969iYwt1AQQ0ovHnEW0YW9TiBfefLvErg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/app-check-interop-types": "0.3.2",
+ "@firebase/auth-interop-types": "0.2.3",
+ "@firebase/component": "0.6.9",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.10.0",
+ "faye-websocket": "0.11.4",
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@firebase/database-compat": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.8.tgz",
+ "integrity": "sha512-OpeWZoPE3sGIRPBKYnW9wLad25RaWbGyk7fFQe4xnJQKRzlynWeFBSRRAoLE2Old01WXwskUiucNqUUVlFsceg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/component": "0.6.9",
+ "@firebase/database": "1.0.8",
+ "@firebase/database-types": "1.0.5",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.10.0",
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@firebase/database-types": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.5.tgz",
+ "integrity": "sha512-fTlqCNwFYyq/C6W7AJ5OCuq5CeZuBEsEwptnVxlNPkWCo5cTTyukzAHRSO/jaQcItz33FfYrrFk1SJofcu2AaQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@firebase/app-types": "0.9.2",
+ "@firebase/util": "1.10.0"
+ }
+ },
+ "node_modules/@firebase/logger": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.2.tgz",
+ "integrity": "sha512-Q1VuA5M1Gjqrwom6I6NUU4lQXdo9IAQieXlujeHZWvRt1b7qQ0KwBaNAjgxG27jgF9/mUwsNmO8ptBCGVYhB0A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@firebase/util": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.10.0.tgz",
+ "integrity": "sha512-xKtx4A668icQqoANRxyDLBLz51TAbDP9KRfpbKGxiCAW346d0BeJe5vN6/hKxxmWwnZ0mautyv39JxviwwQMOQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@google-cloud/firestore": {
+ "version": "7.11.6",
+ "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.6.tgz",
+ "integrity": "sha512-EW/O8ktzwLfyWBOsNuhRoMi8lrC3clHM5LVFhGvO1HCsLozCOOXRAlHrYBoE6HL42Sc8yYMuCb2XqcnJ4OOEpw==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@opentelemetry/api": "^1.3.0",
+ "fast-deep-equal": "^3.1.1",
+ "functional-red-black-tree": "^1.0.1",
+ "google-gax": "^4.3.3",
+ "protobufjs": "^7.2.6"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@google-cloud/paginator": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.2.tgz",
+ "integrity": "sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "arrify": "^2.0.0",
+ "extend": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@google-cloud/projectify": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz",
+ "integrity": "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@google-cloud/promisify": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz",
+ "integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@google-cloud/storage": {
+ "version": "7.21.0",
+ "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.21.0.tgz",
+ "integrity": "sha512-l+IFTkd+6Y5LoAuXyYCKNAKtw/Ci+rAMqgdTB1jv4iZiLhw0rtq+0qjIRbBizXkNzEFmXiXUW0H7sZQQvk1ffA==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@google-cloud/paginator": "^5.0.0",
+ "@google-cloud/projectify": "^4.0.0",
+ "@google-cloud/promisify": "<4.1.0",
+ "abort-controller": "^3.0.0",
+ "async-retry": "^1.3.3",
+ "duplexify": "^4.1.3",
+ "fast-xml-parser": "^5.3.4",
+ "gaxios": "^6.0.2",
+ "google-auth-library": "^9.6.3",
+ "html-entities": "^2.5.2",
+ "mime": "^3.0.0",
+ "p-limit": "^3.0.1",
+ "retry-request": "^7.0.0",
+ "teeny-request": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@grpc/grpc-js": {
+ "version": "1.14.4",
+ "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.4.tgz",
+ "integrity": "sha512-k9Dj3DV/itK9D06Y8f190Qgop7/Ui+D0njFV3LHMPwPT75DpXLQohE9Wmz0QElrJnzsjB7KPWiKJbOl7IPDArQ==",
+ "devOptional": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@grpc/proto-loader": "^0.8.0",
+ "@js-sdsl/ordered-map": "^4.4.2"
+ },
+ "engines": {
+ "node": ">=12.10.0"
+ }
+ },
+ "node_modules/@grpc/grpc-js/node_modules/@grpc/proto-loader": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.1.tgz",
+ "integrity": "sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg==",
+ "devOptional": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "lodash.camelcase": "^4.3.0",
+ "long": "^5.0.0",
+ "protobufjs": "^7.5.5",
+ "yargs": "^17.7.2"
+ },
+ "bin": {
+ "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@grpc/proto-loader": {
+ "version": "0.7.15",
+ "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz",
+ "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==",
+ "devOptional": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "lodash.camelcase": "^4.3.0",
+ "long": "^5.0.0",
+ "protobufjs": "^7.2.5",
+ "yargs": "^17.7.2"
+ },
+ "bin": {
+ "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@hono/node-server": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.0.tgz",
+ "integrity": "sha512-XovyyCCnBzW+zKu+z/zq8hwNs4KOR5rEMAOxo2f40Q5xoOI37IMm6MIg2COOUtUApo0i6850MTBKH2u4QLGIqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "peerDependencies": {
+ "hono": "^4"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
+ "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/types": "^0.15.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.8",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz",
+ "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.2",
+ "@humanfs/types": "^0.15.0",
+ "@humanwhocodes/retry": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/types": {
+ "version": "0.15.0",
+ "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz",
+ "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@ioredis/commands": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.10.0.tgz",
+ "integrity": "sha512-UmeW7z4LfctwoQ5wkhVzgq8tXkreED2xZGpX+Bg+zA+WJFZCT6c062AfCK/Dfk81xZnnwdhJCUMkitihRaoC2Q==",
+ "license": "MIT"
+ },
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "license": "MIT"
+ },
+ "node_modules/@isaacs/cliui/node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "license": "MIT",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@istanbuljs/schema": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz",
+ "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "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",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "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"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "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/@js-sdsl/ordered-map": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz",
+ "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==",
+ "devOptional": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/js-sdsl"
+ }
+ },
+ "node_modules/@kwsites/file-exists": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz",
+ "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.1.1"
+ }
+ },
+ "node_modules/@modelcontextprotocol/sdk": {
+ "version": "1.30.0",
+ "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.30.0.tgz",
+ "integrity": "sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA==",
+ "license": "MIT",
+ "dependencies": {
+ "@hono/node-server": "^1.19.9 || ^2.0.5",
+ "ajv": "^8.17.1",
+ "ajv-formats": "^3.0.1",
+ "content-type": "^1.0.5",
+ "cors": "^2.8.5",
+ "cross-spawn": "^7.0.5",
+ "eventsource": "^3.0.2",
+ "eventsource-parser": "^3.0.0",
+ "express": "^5.2.1",
+ "express-rate-limit": "^8.2.1",
+ "hono": "^4.11.4",
+ "jose": "^6.1.3",
+ "json-schema-typed": "^8.0.2",
+ "pkce-challenge": "^5.0.0",
+ "raw-body": "^3.0.0",
+ "zod": "^3.25 || ^4.0",
+ "zod-to-json-schema": "^3.25.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@cfworker/json-schema": "^4.1.1",
+ "zod": "^3.25 || ^4.0"
+ },
+ "peerDependenciesMeta": {
+ "@cfworker/json-schema": {
+ "optional": true
+ },
+ "zod": {
+ "optional": false
+ }
+ }
+ },
+ "node_modules/@modelcontextprotocol/sdk/node_modules/jose": {
+ "version": "6.2.8",
+ "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.8.tgz",
+ "integrity": "sha512-Bsdjwm3Qsd/P0jR+BHDe3LytDfY7WBq2HmCCLIwuVRHMuEC9ae7/R474GIUdF1NgCyZjzVo/A9DOiOBtXq8ZoQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/panva"
+ }
+ },
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.1.tgz",
+ "integrity": "sha512-KjZdi8Q1wh89gsVmghvbrMgWl6ZWmRmHV6wjB7/g4Zf0dyO+hH3neZUtuDNPO00qq5YE5RITVWvrIZKRaAmzGQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@tybys/wasm-util": "^0.10.3"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=23.5.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "peerDependencies": {
+ "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.3",
+ "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.3"
+ }
+ },
+ "node_modules/@noble/hashes": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
+ "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^14.21.3 || >=16"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@nodable/entities": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-3.0.0.tgz",
+ "integrity": "sha512-8L9xFeTYKhm49xfIypoe2W5wV1m/3Z58kT+7kR9A8OyFxcPduI4VmxaUMQyKYrRjUoLLSXv6EKKID5Tvj9cUVw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodable"
+ }
+ ],
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@notifkit/mcp": {
+ "resolved": "packages/mcp",
+ "link": true
+ },
+ "node_modules/@notifkit/provider-console": {
+ "resolved": "packages/provider-console",
+ "link": true
+ },
+ "node_modules/@notifkit/provider-fcm": {
+ "resolved": "packages/provider-fcm",
+ "link": true
+ },
+ "node_modules/@notifkit/provider-resend": {
+ "resolved": "packages/provider-resend",
+ "link": true
+ },
+ "node_modules/@one-ini/wasm": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz",
+ "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==",
+ "license": "MIT"
+ },
+ "node_modules/@opentelemetry/api": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz",
+ "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/@oxc-project/types": {
+ "version": "0.142.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.142.0.tgz",
+ "integrity": "sha512-7W+2q5AKQVU36fkaryontrHn3YDt1RyUYXatw9i5H8ocYe2sPKSFB6eS8WNPeRKiN1qAWWZUPm7gwFzJGrccqQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/Boshen"
+ }
+ },
+ "node_modules/@paralleldrive/cuid2": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz",
+ "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@noble/hashes": "^1.1.5"
+ }
+ },
+ "node_modules/@pinojs/redact": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz",
+ "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==",
+ "license": "MIT"
+ },
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@protobufjs/aspromise": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+ "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
+ "devOptional": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/base64": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+ "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
+ "devOptional": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/codegen": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz",
+ "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==",
+ "devOptional": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/eventemitter": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz",
+ "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==",
+ "devOptional": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/fetch": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz",
+ "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==",
+ "devOptional": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.1"
+ }
+ },
+ "node_modules/@protobufjs/float": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
+ "devOptional": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/path": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+ "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
+ "devOptional": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/pool": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+ "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
+ "devOptional": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/utf8": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.2.tgz",
+ "integrity": "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==",
+ "devOptional": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@quansync/fs": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@quansync/fs/-/fs-1.0.0.tgz",
+ "integrity": "sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "quansync": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sxzz"
+ }
+ },
+ "node_modules/@react-email/render": {
+ "version": "0.0.16",
+ "resolved": "https://registry.npmjs.org/@react-email/render/-/render-0.0.16.tgz",
+ "integrity": "sha512-wDaMy27xAq1cJHtSFptp0DTKPuV2GYhloqia95ub/DH9Dea1aWYsbdM918MOc/b/HvVS3w1z8DWzfAk13bGStQ==",
+ "license": "MIT",
+ "dependencies": {
+ "html-to-text": "9.0.5",
+ "js-beautify": "^1.14.11",
+ "react-promise-suspense": "0.3.4"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0"
+ }
+ },
+ "node_modules/@rolldown/binding-android-arm64": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.1.tgz",
+ "integrity": "sha512-02hOeOSryYxVrOIphmLAsqnCJWxwlzFk+pEt/N/i6OgT3lShHO7xGCU5cpgchRDHboAEbSjzgGh+O/u1GswQmA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-arm64": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.1.tgz",
+ "integrity": "sha512-fMsTOnN0OjFm3CyppWPitKnc8UlliVARUULW6cfU6AIqjdtgmSFWSk9vecHzZduv/yMWIHDlRhM1e8Iff9uAfA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-darwin-x64": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.1.tgz",
+ "integrity": "sha512-1wjKdz/XLGKHaTNHjQveQ/B23TKx4ItAqm1JbyVuvNPc4Ze0Fb48s49TAd/2zcplPl8okE/UbTgmlVfwT7eFeQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-freebsd-x64": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.1.tgz",
+ "integrity": "sha512-Fa0jHR07E7YBN4vOEsbVf2briYNsuOowfLJaXULZM0ldMlaCaj2LJgLMbMe4iacRyZmvR8efFhgR9wKuGclQUg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.1.tgz",
+ "integrity": "sha512-pzkgu1SSHGgRRyRZ4fbmSgmajbVt+epaLP99NDjFft69v/ypfTi6swBMiVdh2EkQ0OSnHE1lZDM7DRGkyAzUpA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-gnu": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.1.tgz",
+ "integrity": "sha512-QI5SEDY8cbiYWHx0VO4vIc3UlS6a32vXHjU8Qy/17adEmZIPuByJg13UEvo9c/UCiUkdcVWY83C+b+JrwnNyUg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-arm64-musl": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.1.tgz",
+ "integrity": "sha512-Sm41FyCeXqmYcERoYOCbGIL5hNfd8w9LQ7Y61Bev48HkcjaJqV/iiVOaiDxjVTRMS+QKrZmD8cfPt4uMVnvM+A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-ppc64-gnu": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.1.tgz",
+ "integrity": "sha512-2x+WhXTGl9yJYPbltW/BSEPTVz9OIWQyER4N+gJEDWkkn904eRcBzELqh/Hf7K0w/ubGbKNMv0ZC+94QK/IFEg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-s390x-gnu": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.1.tgz",
+ "integrity": "sha512-eEjmQpuRQayHPWWnywaWHkFT3ToPbP3RYy42VVd/B9aBGDA+Ol25EIWHxKQST3IiWJjikCWUF7KtbfqwZrzVwQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-gnu": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.1.tgz",
+ "integrity": "sha512-/Orga1fZYkLc/56jBICcHrKchl8Z2UKdDSr3LG9ToWO1lQ6a4Livk9Xz+9WN91zsz5QR3XQz2NNoSDEvP6qadw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-linux-x64-musl": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.1.tgz",
+ "integrity": "sha512-xxBJRL+0q0Kce7orznGWLuylHDY65vuARXZRpX+hPdv+DqK2c3NlCsVA98tlWzWNEE7yPqA/1NQ5nnCrj49Y5A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-openharmony-arm64": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.1.tgz",
+ "integrity": "sha512-M6AdXIXw3s+/8XpKMzdGDEXGS1S7kwUsy+rcTIUIOx5Ge4nXKCtAFHFV9YKkXvGcC5WMoTjAteLzlsQROVI0Yw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-wasm32-wasi": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.2.1.tgz",
+ "integrity": "sha512-/TX0SoRGojHzSAHpfVBbavRVSazg5U3h3Y3VXfcc0cdugq6kxdqw8LPGFiPr+/7gE/60zRcsOY2Vi9b9eT0jww==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "2.0.0-alpha.3",
+ "@emnapi/runtime": "2.0.0-alpha.3",
+ "@napi-rs/wasm-runtime": "^1.2.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=23.5.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-arm64-msvc": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.1.tgz",
+ "integrity": "sha512-EvRrivJieyHG+AO9lleZWgq+g0+S7oV2C51yuqlcyU/R9net+sI4Pj0F+lUoP2bEr6TWX3SqFaaS0SzfLxSzkw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-x64-msvc": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.1.tgz",
+ "integrity": "sha512-Z4eCmn5QJ/5+azF9knpLWKfVd9aidn0mAe9TpJgvBLId9Ax3t0+JVxBmT25Bv7NBbVW1TZyKjQjQReouMeH5UQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
+ "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz",
+ "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz",
+ "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz",
+ "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz",
+ "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz",
+ "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz",
+ "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz",
+ "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz",
+ "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz",
+ "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz",
+ "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz",
+ "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz",
+ "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz",
+ "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz",
+ "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz",
+ "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz",
+ "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz",
+ "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz",
+ "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz",
+ "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz",
+ "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz",
+ "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz",
+ "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz",
+ "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz",
+ "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz",
+ "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@selderee/plugin-htmlparser2": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz",
+ "integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==",
+ "license": "MIT",
+ "dependencies": {
+ "domhandler": "^5.0.3",
+ "selderee": "^0.11.0"
+ },
+ "funding": {
+ "url": "https://ko-fi.com/killymxi"
+ }
+ },
+ "node_modules/@stablelib/base64": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz",
+ "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==",
+ "license": "MIT"
+ },
+ "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"
+ },
+ "node_modules/@testcontainers/postgresql": {
+ "version": "12.0.4",
+ "resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-12.0.4.tgz",
+ "integrity": "sha512-a/pLU6j5lpKKAlUTPwqweqMGhOSjgTSb6HBX69TOrXn32ifU37nnQDmNFTj8ddOAw+BQL9oTRkeOxVbZkqhgZA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "testcontainers": "^12.0.4"
+ }
+ },
+ "node_modules/@testcontainers/redis": {
+ "version": "12.0.4",
+ "resolved": "https://registry.npmjs.org/@testcontainers/redis/-/redis-12.0.4.tgz",
+ "integrity": "sha512-44Ov3Lcptb3KqljcCy6xIJ2VxNsN+3Kw/YpBiotZpOcI4iLgLNmcOgvCFPVo+vDoh4rCfgtNUEbIpL5Mu7oRuw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "testcontainers": "^12.0.4"
+ }
+ },
+ "node_modules/@tootallnate/once": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.1.tgz",
+ "integrity": "sha512-HqmEUIGRJ5fSXchkVgR5F7qn48bDBzv0kWj/Kfu5e6uci4UlEeng4331LnBkWffb++Ei3FOVLxo8JJWMFBDMeQ==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz",
+ "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@types/caseless": {
+ "version": "0.12.5",
+ "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz",
+ "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@types/conventional-commits-parser": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.2.tgz",
+ "integrity": "sha512-BgT2szDXnVypgpNxOK8aL5SGjUdaQbC++WZNjF1Qge3Og2+zhHj+RWhmehLhYyvQwqAmvezruVfOf8+3m74W+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/cookiejar": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz",
+ "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/docker-modem": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.6.tgz",
+ "integrity": "sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*",
+ "@types/ssh2": "*"
+ }
+ },
+ "node_modules/@types/dockerode": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-4.0.1.tgz",
+ "integrity": "sha512-cmUpB+dPN955PxBEuXE3f6lKO1hHiIGYJA46IVF3BJpNsZGvtBDcRnlrHYHtOH/B6vtDOyl2kZ2ShAu3mgc27Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/docker-modem": "*",
+ "@types/node": "*",
+ "@types/ssh2": "*"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/jsonwebtoken": {
+ "version": "9.0.10",
+ "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz",
+ "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/ms": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/long": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
+ "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@types/methods": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz",
+ "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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": "22.20.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz",
+ "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==",
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/@types/request": {
+ "version": "2.48.13",
+ "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.13.tgz",
+ "integrity": "sha512-FGJ6udDNUCjd19pp0Q3iTiDkwhYup7J8hpMW9c4k53NrccQFFWKRho6hvtPPEhnXWKvukfwAlB6DbDz4yhH5Gg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@types/caseless": "*",
+ "@types/node": "*",
+ "@types/tough-cookie": "*",
+ "form-data": "^2.5.5"
+ }
+ },
+ "node_modules/@types/ssh2": {
+ "version": "1.15.5",
+ "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.15.5.tgz",
+ "integrity": "sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "^18.11.18"
+ }
+ },
+ "node_modules/@types/ssh2-streams": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/@types/ssh2-streams/-/ssh2-streams-0.1.13.tgz",
+ "integrity": "sha512-faHyY3brO9oLEA0QlcO8N2wT7R0+1sHWZvQ+y3rMLwdY1ZyS1z0W3t65j9PqT4HmQ6ALzNe7RZlNuCNE0wBSWA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/ssh2/node_modules/@types/node": {
+ "version": "18.19.130",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz",
+ "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "node_modules/@types/ssh2/node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/superagent": {
+ "version": "8.1.11",
+ "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.11.tgz",
+ "integrity": "sha512-KA7srSW/HENDtOw9DOqaFLgWuMqN9WgjEw62lh9dpvRaZDkhdOkazASd7X7i2eMUYLHa1U37ZttnePsH5zTDHw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/cookiejar": "^2.1.5",
+ "@types/methods": "^1.1.4",
+ "@types/node": "*",
+ "form-data": "^4.0.0"
+ }
+ },
+ "node_modules/@types/superagent/node_modules/form-data": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz",
+ "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.4",
+ "mime-types": "^2.1.35"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/@types/supertest": {
+ "version": "7.2.1",
+ "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-7.2.1.tgz",
+ "integrity": "sha512-4CbBvoYVLHL7+yhbYrZET0vsvuyXTC05aRe7dNQkwMzm56auceoy6Yu3K50uZmwfHna1os3CMSgM/3QVkUtPTw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/methods": "^1.1.4",
+ "@types/superagent": "^8.1.0"
+ }
+ },
+ "node_modules/@types/tough-cookie": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz",
+ "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.64.0.tgz",
+ "integrity": "sha512-CGvQPBxN3wZLu6Rz2kFUpZeoCm78xUic92ck39KPePkO1NPOwjCqdQnm5Q87tpWw9vcBvW8XLrDXjH9PWYtJ3Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.12.2",
+ "@typescript-eslint/scope-manager": "8.64.0",
+ "@typescript-eslint/type-utils": "8.64.0",
+ "@typescript-eslint/utils": "8.64.0",
+ "@typescript-eslint/visitor-keys": "8.64.0",
+ "ignore": "^7.0.5",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.64.0",
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.64.0.tgz",
+ "integrity": "sha512-KA0OshtlcCCXmbfqyZkM5pV3/WNraJf7DkJRLpyrmwPtud57H5BDX7C3k0LPSPxpprfRL+cJDGabF10mvNCoCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.64.0",
+ "@typescript-eslint/types": "8.64.0",
+ "@typescript-eslint/typescript-estree": "8.64.0",
+ "@typescript-eslint/visitor-keys": "8.64.0",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.64.0.tgz",
+ "integrity": "sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.64.0",
+ "@typescript-eslint/types": "^8.64.0",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.64.0.tgz",
+ "integrity": "sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.64.0",
+ "@typescript-eslint/visitor-keys": "8.64.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.64.0.tgz",
+ "integrity": "sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.64.0.tgz",
+ "integrity": "sha512-XWG4Fmmv/6SvyS9nH8jWrKs6terwJvE8cyRt1CzYYqzp9OrPhCT4cMc/f7C6RZCwG+qMmiffJS1/qJP8G1URtg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.64.0",
+ "@typescript-eslint/typescript-estree": "8.64.0",
+ "@typescript-eslint/utils": "8.64.0",
+ "debug": "^4.4.3",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.64.0.tgz",
+ "integrity": "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.64.0.tgz",
+ "integrity": "sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.64.0",
+ "@typescript-eslint/tsconfig-utils": "8.64.0",
+ "@typescript-eslint/types": "8.64.0",
+ "@typescript-eslint/visitor-keys": "8.64.0",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.64.0.tgz",
+ "integrity": "sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.9.1",
+ "@typescript-eslint/scope-manager": "8.64.0",
+ "@typescript-eslint/types": "8.64.0",
+ "@typescript-eslint/typescript-estree": "8.64.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.64.0.tgz",
+ "integrity": "sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.64.0",
+ "eslint-visitor-keys": "^5.0.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "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/@vitest/coverage-v8": {
+ "version": "2.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.9.tgz",
+ "integrity": "sha512-Z2cOr0ksM00MpEfyVE8KXIYPEcBFxdbLSs56L8PO0QQMxt/6bDj45uQfxoc96v05KW3clk7vvgP0qfDit9DmfQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@ampproject/remapping": "^2.3.0",
+ "@bcoe/v8-coverage": "^0.2.3",
+ "debug": "^4.3.7",
+ "istanbul-lib-coverage": "^3.2.2",
+ "istanbul-lib-report": "^3.0.1",
+ "istanbul-lib-source-maps": "^5.0.6",
+ "istanbul-reports": "^3.1.7",
+ "magic-string": "^0.30.12",
+ "magicast": "^0.3.5",
+ "std-env": "^3.8.0",
+ "test-exclude": "^7.0.1",
+ "tinyrainbow": "^1.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "@vitest/browser": "2.1.9",
+ "vitest": "2.1.9"
+ },
+ "peerDependenciesMeta": {
+ "@vitest/browser": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vitest/expect": {
+ "version": "2.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz",
+ "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/spy": "2.1.9",
+ "@vitest/utils": "2.1.9",
+ "chai": "^5.1.2",
+ "tinyrainbow": "^1.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/mocker": {
+ "version": "2.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz",
+ "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/spy": "2.1.9",
+ "estree-walker": "^3.0.3",
+ "magic-string": "^0.30.12"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "msw": "^2.4.9",
+ "vite": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "msw": {
+ "optional": true
+ },
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vitest/pretty-format": {
+ "version": "2.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz",
+ "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tinyrainbow": "^1.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/runner": {
+ "version": "2.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz",
+ "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/utils": "2.1.9",
+ "pathe": "^1.1.2"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/runner/node_modules/pathe": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
+ "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@vitest/snapshot": {
+ "version": "2.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz",
+ "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "2.1.9",
+ "magic-string": "^0.30.12",
+ "pathe": "^1.1.2"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/snapshot/node_modules/pathe": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
+ "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@vitest/spy": {
+ "version": "2.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz",
+ "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tinyspy": "^3.0.2"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/utils": {
+ "version": "2.1.9",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz",
+ "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "2.1.9",
+ "loupe": "^3.1.2",
+ "tinyrainbow": "^1.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@workflow/serde": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/@workflow/serde/-/serde-4.1.0.tgz",
+ "integrity": "sha512-pav4F2BoirECWR7Nf1TKt+2eETcBj7jj4cBefQ8VXQCA6NPkaKeLfj/zMgi+3zYV5ZIBT4GuUiphsj0/b9hPQQ==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@yuku-codegen/binding-darwin-arm64": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-darwin-arm64/-/binding-darwin-arm64-0.8.1.tgz",
+ "integrity": "sha512-Ck0RmDBLc+R0dZpfTXmJFAvcuPf+npbJF1T/VhC9pPDGjNEOGwn4DwFcjhg//DfNIO+xgktsLSt5S4v0MFrToA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@yuku-codegen/binding-darwin-x64": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-darwin-x64/-/binding-darwin-x64-0.8.1.tgz",
+ "integrity": "sha512-6BDLLWi8wHbCghgkajS+OeVJX2jLY9pY9HV/qH9daLP1XCyMSPssEiXjKyPiJq+St5b+zcPgMEBF8cgJDNhMMQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@yuku-codegen/binding-freebsd-x64": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-freebsd-x64/-/binding-freebsd-x64-0.8.1.tgz",
+ "integrity": "sha512-Rd1PHfw2mxC8y00kWxYWDClnBC1NbSwPuq0zKLYPggE7mlOnV+eQ15futtaz/Q4BP+jOtEv9elxvP7by20h6Kw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@yuku-codegen/binding-linux-arm-gnu": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-linux-arm-gnu/-/binding-linux-arm-gnu-0.8.1.tgz",
+ "integrity": "sha512-/XGncN6jQ/CwXNgWwo+XnsUmILY1q1mbS1f38XnYOHw3DOKOISxVIAlD9IsmdrHLAUAncFlgFS3ky4OO9fvjyg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@yuku-codegen/binding-linux-arm-musl": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-linux-arm-musl/-/binding-linux-arm-musl-0.8.1.tgz",
+ "integrity": "sha512-Rxut53qx3JbTkQ8+A6I8Lrplwz55HQVCjEsTsWF1uOpfgALdvuKSMPIE0gVqefEEcOSA9ASklbNacmXwiqnaMw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@yuku-codegen/binding-linux-arm64-gnu": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.8.1.tgz",
+ "integrity": "sha512-hBNkUPHX04BjB2oIIgIlYT2tog9x0rmBVkZep0f4LLYyFhdJfYM/s2pJ0c8ehQhhkQaJFokW4CxGhiVvVLlzWg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@yuku-codegen/binding-linux-arm64-musl": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.8.1.tgz",
+ "integrity": "sha512-ePZZnmOxArA4jA3AWlJj3WdY5z1ahQSLwAYrv7mozkJVcrBz2wZgy8l9ukcHFpTxPyhxCAea4gBSZEqqsCYE3A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@yuku-codegen/binding-linux-x64-gnu": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.8.1.tgz",
+ "integrity": "sha512-LGAEXnQ4Xg/BtOuEirABwa9ByUTXFjealqXXAHVFb8EEmZu5QeHLtsdB2MA2IcYVAgW/QPkyqfnd80R4FltI2A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@yuku-codegen/binding-linux-x64-musl": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-linux-x64-musl/-/binding-linux-x64-musl-0.8.1.tgz",
+ "integrity": "sha512-PPDMcQIKCNTOaLBBKh9SBM1EAPcsvnlShMAxq2D/twnSCSEUI9LfrR+G9J4P3o+46ZS4WN8mjZsj8p4PKOnKgw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@yuku-codegen/binding-win32-arm64": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-win32-arm64/-/binding-win32-arm64-0.8.1.tgz",
+ "integrity": "sha512-W8A1QpstKYhg4e56s13L4ByQprQ1OXY3QFoNPIh6+4v/DEgqfhJ9ApU++e7zHQCQrhYjyQC/uWtxUcRqpyL/7Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@yuku-codegen/binding-win32-x64": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-win32-x64/-/binding-win32-x64-0.8.1.tgz",
+ "integrity": "sha512-zH7g7pLjfl3uZaNvYzd1PnCerU9fvhS16B7Z0z4Ai0S/oaGI53Y2xh8ylFDvDvmquEP7vay4O/zuLgvZeEsxVA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@yuku-parser/binding-darwin-arm64": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@yuku-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.8.1.tgz",
+ "integrity": "sha512-iKQinrhsdxsGJ38msw4KaD7yotWw5N2nqMLeOPD+ttpZQKJc9nGL3VHMtISzCJ9Q09g0N3m91mYRFnIbXfhhig==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@yuku-parser/binding-darwin-x64": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@yuku-parser/binding-darwin-x64/-/binding-darwin-x64-0.8.1.tgz",
+ "integrity": "sha512-k7zt2H3sWbYzwTGAK2TT6ma18w1XpwFAWgmBHiaMGHVe63aArrtO+55r6g7chXJ4joB6lb8SxYFyRQ974xfawQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@yuku-parser/binding-freebsd-x64": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@yuku-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.8.1.tgz",
+ "integrity": "sha512-tQjrlkE/vYEBXZ+pM59FerIVD4Zfs2OpRvN3e8oo12pTcAbwTTxteZKWaedfUsZr+gKO4tLLAaFaFIwa5fQJFw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@yuku-parser/binding-linux-arm-gnu": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@yuku-parser/binding-linux-arm-gnu/-/binding-linux-arm-gnu-0.8.1.tgz",
+ "integrity": "sha512-LZQgyer5d5i1f5oot+x6EhANXPD4FeCgz2CiSiWFSFBeG1U2m5jz65LD8gZwzy4CP4QVxQFyX9BazVRskTbwuQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@yuku-parser/binding-linux-arm-musl": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@yuku-parser/binding-linux-arm-musl/-/binding-linux-arm-musl-0.8.1.tgz",
+ "integrity": "sha512-69G1N4bPkygrus/ahtFxVdv9lzfvS65fuyMdDknn5BZU/gfFGBJyHeJ8UGgLPTyR3YptlJMuZhEkdgp6Qhfz2g==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@yuku-parser/binding-linux-arm64-gnu": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@yuku-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.8.1.tgz",
+ "integrity": "sha512-XL5+JpKCl1ghQTnZezy41NzmPdk4rZ6s0Z8qvGsgNPJ6yrSD7Ne8iz20SZZd/nW720qHiJy/ux62rwzZ+/clMg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@yuku-parser/binding-linux-arm64-musl": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@yuku-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.8.1.tgz",
+ "integrity": "sha512-Rj290yfrWlrV4/+HrAuoeJXMHdoNubJWKdhg1QDa/mvqa8MUYFd4IHuVYjP/WdQzEgOAWGrLC48vcnge6U3jbg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@yuku-parser/binding-linux-x64-gnu": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@yuku-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.8.1.tgz",
+ "integrity": "sha512-d3IDkhAxiUTVuGI6BKIMKUyLe7ZxMY5yLqyvvTqJD1WPnE0eJTsLeRFRBv7/HXsnAzDs0IgvoG5bjvUkHZ5kSg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@yuku-parser/binding-linux-x64-musl": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@yuku-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.8.1.tgz",
+ "integrity": "sha512-Oyp+2gGYKK/AvN2ZoauRn1LGPdkjME5/TE/l4a9mT0JdNeiRTvno42383bt04MWnAZcYS4Hv7YsiFOXUBeLdpA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@yuku-parser/binding-win32-arm64": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@yuku-parser/binding-win32-arm64/-/binding-win32-arm64-0.8.1.tgz",
+ "integrity": "sha512-H77lYgICWRZycGmLaojzg0aSs3Z3v8SZWkcEjl1Ql3s+Km2SyiQcZ7RGq++Wh5nJxpiDJGAzYDC74XqMNm2ZHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@yuku-parser/binding-win32-x64": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@yuku-parser/binding-win32-x64/-/binding-win32-x64-0.8.1.tgz",
+ "integrity": "sha512-ABniFzqYYn64NUEgmHTb6JxEzAlpmPu4cpMFjMBlR6cJXqL+vd7K1ae/yrt+NhUduHkJWJqG4lbHbV2yp9el3w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@yuku-toolchain/types": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@yuku-toolchain/types/-/types-0.8.1.tgz",
+ "integrity": "sha512-HcGEV3kOn9evBTm2ARYOFNKAI7sY9twQozCVd9EVdlsBlnKi0a2iv8xXxYVHFCBQpX3SvvPXBhk8lcK6NUTdNg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/abbrev": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz",
+ "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==",
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/abort-controller": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+ "license": "MIT",
+ "dependencies": {
+ "event-target-shim": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=6.5"
+ }
+ },
+ "node_modules/accepts": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
+ "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "^3.0.0",
+ "negotiator": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/accepts/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",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/accepts/node_modules/mime-types": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
+ "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "^1.54.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.17.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz",
+ "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/ai": {
+ "version": "7.0.37",
+ "resolved": "https://registry.npmjs.org/ai/-/ai-7.0.37.tgz",
+ "integrity": "sha512-stF+SEQJgKY3Qfe3FwNzqUrehHviOp2l7LemoI8YMOa0Zk5PxKsRNgUk3cHXz0RAue9RRyCAis2fz6LSCP6EKw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@ai-sdk/gateway": "4.0.28",
+ "@ai-sdk/provider": "4.0.3",
+ "@ai-sdk/provider-utils": "5.0.12"
+ },
+ "engines": {
+ "node": ">=22"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.76 || ^4.1.8"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "8.20.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
+ "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-formats": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
+ "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ansi-escapes": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz",
+ "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "environment": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "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/ansis": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.3.1.tgz",
+ "integrity": "sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/anynum": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz",
+ "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/archiver": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz",
+ "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "archiver-utils": "^5.0.2",
+ "async": "^3.2.4",
+ "buffer-crc32": "^1.0.0",
+ "readable-stream": "^4.0.0",
+ "readdir-glob": "^1.1.2",
+ "tar-stream": "^3.0.0",
+ "zip-stream": "^6.0.1"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/archiver-utils": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz",
+ "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "glob": "^10.0.0",
+ "graceful-fs": "^4.2.0",
+ "is-stream": "^2.0.1",
+ "lazystream": "^1.0.0",
+ "lodash": "^4.17.15",
+ "normalize-path": "^3.0.0",
+ "readable-stream": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/archiver-utils/node_modules/readable-stream": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
+ "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "abort-controller": "^3.0.0",
+ "buffer": "^6.0.3",
+ "events": "^3.3.0",
+ "process": "^0.11.10",
+ "string_decoder": "^1.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/archiver/node_modules/readable-stream": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
+ "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "abort-controller": "^3.0.0",
+ "buffer": "^6.0.3",
+ "events": "^3.3.0",
+ "process": "^0.11.10",
+ "string_decoder": "^1.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/array-ify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz",
+ "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/arrify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
+ "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/asap": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/asn1": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
+ "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": "~2.1.0"
+ }
+ },
+ "node_modules/assertion-error": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+ "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/async": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
+ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/async-lock": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz",
+ "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/async-retry": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz",
+ "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "retry": "0.13.1"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "devOptional": true,
+ "license": "MIT"
+ },
+ "node_modules/atomic-sleep": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
+ "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/b4a": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz",
+ "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "react-native-b4a": "*"
+ },
+ "peerDependenciesMeta": {
+ "react-native-b4a": {
+ "optional": true
+ }
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/bare-events": {
+ "version": "2.9.1",
+ "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz",
+ "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "bare-abort-controller": "*"
+ },
+ "peerDependenciesMeta": {
+ "bare-abort-controller": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/bare-fs": {
+ "version": "4.7.4",
+ "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.7.4.tgz",
+ "integrity": "sha512-y1kC+ffIx/tPLdTE693uNjHfzTfr+ravR5tvWlMXe25nELbkqV400S71qHDwbkAQ1FVEZobB1NFRzFbCCcyBCQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-events": "^2.5.4",
+ "bare-path": "^3.0.0",
+ "bare-stream": "^2.6.4",
+ "bare-url": "^2.2.2",
+ "fast-fifo": "^1.3.2"
+ },
+ "engines": {
+ "bare": ">=1.16.0"
+ },
+ "peerDependencies": {
+ "bare-buffer": "*"
+ },
+ "peerDependenciesMeta": {
+ "bare-buffer": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/bare-path": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.1.tgz",
+ "integrity": "sha512-JprUlveX3QjApC1cTpsUOiscADftCGVWkzitbHsRqv84hzYwYHw2mbluddsq5TvI8mH/8Ov1f4BiMAdcB0oYnQ==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/bare-stream": {
+ "version": "2.13.3",
+ "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.3.tgz",
+ "integrity": "sha512-Kc+brLqvEqGkjyfiwJmImAOqLZL7OsoLKuavx+hJjgVV3nLTOjloJyPMFxjUPerGGHrNH0fLU06jjykMLWrERQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "b4a": "^1.8.1",
+ "streamx": "^2.25.0",
+ "teex": "^1.0.1"
+ },
+ "peerDependencies": {
+ "bare-abort-controller": "*",
+ "bare-buffer": "*",
+ "bare-events": "*"
+ },
+ "peerDependenciesMeta": {
+ "bare-abort-controller": {
+ "optional": true
+ },
+ "bare-buffer": {
+ "optional": true
+ },
+ "bare-events": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/bare-url": {
+ "version": "2.4.5",
+ "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.5.tgz",
+ "integrity": "sha512-K+y9xF1tN+CdPu4qWwr0QiK1Al07eFPGYK5M2pDXcmHdMdgC/tT/bpmMe1hrmRHaidKLkXrC+cRNYf3XVDUhSQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-path": "^3.0.0"
+ }
+ },
+ "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"
+ },
+ "node_modules/bcrypt-pbkdf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+ "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "tweetnacl": "^0.14.3"
+ }
+ },
+ "node_modules/bignumber.js": {
+ "version": "9.3.1",
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz",
+ "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/bintrees": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz",
+ "integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==",
+ "license": "MIT"
+ },
+ "node_modules/bl": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ }
+ },
+ "node_modules/bl/node_modules/buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "dev": true,
+ "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",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "node_modules/body-parser": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz",
+ "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "^3.1.2",
+ "content-type": "^2.0.0",
+ "debug": "^4.4.3",
+ "http-errors": "^2.0.1",
+ "iconv-lite": "^0.7.2",
+ "on-finished": "^2.4.1",
+ "qs": "^6.15.2",
+ "raw-body": "^3.0.2",
+ "type-is": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/body-parser/node_modules/content-type": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz",
+ "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "5.0.7",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
+ "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "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",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
+ }
+ },
+ "node_modules/buffer-crc32": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz",
+ "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
+ "license": "BSD-3-Clause"
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/buildcheck": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.7.tgz",
+ "integrity": "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/byline": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz",
+ "integrity": "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/cac": {
+ "version": "6.7.14",
+ "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
+ "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/chai": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz",
+ "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "assertion-error": "^2.0.1",
+ "check-error": "^2.1.1",
+ "deep-eql": "^5.0.1",
+ "loupe": "^3.1.0",
+ "pathval": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/check-error": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz",
+ "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 16"
+ }
+ },
+ "node_modules/chownr": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "restore-cursor": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-truncate": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz",
+ "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "slice-ansi": "^5.0.0",
+ "string-width": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "devOptional": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/cliui/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==",
+ "devOptional": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/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==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/cliui/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==",
+ "devOptional": true,
+ "license": "MIT"
+ },
+ "node_modules/cliui/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==",
+ "devOptional": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/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==",
+ "devOptional": true,
+ "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/cliui/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==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/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==",
+ "devOptional": true,
+ "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/cluster-key-slot": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.1.tgz",
+ "integrity": "sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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/colorette": {
+ "version": "2.0.20",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+ "license": "MIT"
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/commander": {
+ "version": "13.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz",
+ "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/compare-func": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz",
+ "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-ify": "^1.0.0",
+ "dot-prop": "^5.1.0"
+ }
+ },
+ "node_modules/component-emitter": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz",
+ "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/compress-commons": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz",
+ "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "crc-32": "^1.2.0",
+ "crc32-stream": "^6.0.0",
+ "is-stream": "^2.0.1",
+ "normalize-path": "^3.0.0",
+ "readable-stream": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/compress-commons/node_modules/readable-stream": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
+ "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "abort-controller": "^3.0.0",
+ "buffer": "^6.0.3",
+ "events": "^3.3.0",
+ "process": "^0.11.10",
+ "string_decoder": "^1.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/config-chain": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
+ "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ini": "^1.3.4",
+ "proto-list": "~1.2.1"
+ }
+ },
+ "node_modules/config-chain/node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "license": "ISC"
+ },
+ "node_modules/content-disposition": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz",
+ "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/conventional-changelog-angular": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz",
+ "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "compare-func": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/conventional-changelog-conventionalcommits": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz",
+ "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "compare-func": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/conventional-commits-parser": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz",
+ "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-text-path": "^2.0.0",
+ "JSONStream": "^1.3.5",
+ "meow": "^12.0.1",
+ "split2": "^4.0.0"
+ },
+ "bin": {
+ "conventional-commits-parser": "cli.mjs"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
+ "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.6.0"
+ }
+ },
+ "node_modules/cookiejar": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz",
+ "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cors": {
+ "version": "2.8.6",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
+ "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
+ "license": "MIT",
+ "dependencies": {
+ "object-assign": "^4",
+ "vary": "^1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/cosmiconfig": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz",
+ "integrity": "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "env-paths": "^2.2.1",
+ "import-fresh": "^3.3.0",
+ "js-yaml": "^4.1.0",
+ "parse-json": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/d-fischer"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.9.5"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/cosmiconfig-typescript-loader": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.3.0.tgz",
+ "integrity": "sha512-Akr82WH1Wfqatyiqpj8HDkO2o2KmJRu1FhKfSNJP3K4IdXwHfEyL7MOb62i1AGQVLtIQM+iCE9CGOtrfhR+mmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "jiti": "2.6.1"
+ },
+ "engines": {
+ "node": ">=v18"
+ },
+ "peerDependencies": {
+ "@types/node": "*",
+ "cosmiconfig": ">=9",
+ "typescript": ">=5"
+ }
+ },
+ "node_modules/cpu-features": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz",
+ "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "dependencies": {
+ "buildcheck": "~0.0.6",
+ "nan": "^2.19.0"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/crc-32": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
+ "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "crc32": "bin/crc32.njs"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/crc32-stream": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz",
+ "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "crc-32": "^1.2.0",
+ "readable-stream": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/crc32-stream/node_modules/readable-stream": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
+ "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "abort-controller": "^3.0.0",
+ "buffer": "^6.0.3",
+ "events": "^3.3.0",
+ "process": "^0.11.10",
+ "string_decoder": "^1.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "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/dargs": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz",
+ "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/dateformat": {
+ "version": "4.6.3",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz",
+ "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==",
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "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/deep-eql": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
+ "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/deepmerge": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/defu": {
+ "version": "6.1.7",
+ "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz",
+ "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "devOptional": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/denque": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
+ "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/dezalgo": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz",
+ "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "asap": "^2.0.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/docker-compose": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-1.4.2.tgz",
+ "integrity": "sha512-rPHigTKGaEHpkUmfd69QgaOp+Os5vGJwG/Ry8lcr8W/382AmI+z/D7qoa9BybKIkqNppaIbs8RYeHSevdQjWww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yaml": "^2.2.2"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/docker-modem": {
+ "version": "5.0.7",
+ "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.7.tgz",
+ "integrity": "sha512-XJgGhoR/CLpqshm4d3L7rzH6t8NgDFUIIpztYlLHIApeJjMZKYJMz2zxPsYxnejq5h3ELYSw/RBsi3t5h7gNTA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "debug": "^4.1.1",
+ "readable-stream": "^3.5.0",
+ "split-ca": "^1.0.1",
+ "ssh2": "^1.15.0"
+ },
+ "engines": {
+ "node": ">= 8.0"
+ }
+ },
+ "node_modules/dockerode": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-5.0.1.tgz",
+ "integrity": "sha512-avsq/xk4YPIrn0CgleX5bjT9Y8IT1p9PxrNQ++RBQ2WEyFfHCTDsT9kmyxz+H/axnjAwg8wJWEIuPGOUuNupiA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@balena/dockerignore": "^1.0.2",
+ "@grpc/grpc-js": "^1.11.1",
+ "@grpc/proto-loader": "^0.7.13",
+ "docker-modem": "^5.0.7",
+ "protobufjs": "^7.3.2",
+ "tar-fs": "^2.1.4"
+ },
+ "engines": {
+ "node": ">= 14.17"
+ }
+ },
+ "node_modules/dockerode/node_modules/tar-fs": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.5.tgz",
+ "integrity": "sha512-OboTd8mmMhZDNPV+UjQcK9yKAatXu2aJ+r1w4im1Otd4M4fl2hwvdoXUxIYHFTHWK/3y3FarBP70v3vwmGlOxw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chownr": "^1.1.1",
+ "mkdirp-classic": "^0.5.2",
+ "pump": "^3.0.0",
+ "tar-stream": "^2.1.4"
+ }
+ },
+ "node_modules/dockerode/node_modules/tar-stream": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
+ "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bl": "^4.0.3",
+ "end-of-stream": "^1.4.1",
+ "fs-constants": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.1.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/dom-serializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "entities": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/domhandler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "domelementtype": "^2.3.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
+ "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
+ "node_modules/dot-prop": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
+ "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-obj": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "16.6.1",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
+ "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/drizzle-kit": {
+ "version": "0.31.10",
+ "resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.31.10.tgz",
+ "integrity": "sha512-7OZcmQUrdGI+DUNNsKBn1aW8qSoKuTH7d0mYgSP8bAzdFzKoovxEFnoGQp2dVs82EOJeYycqRtciopszwUf8bw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@drizzle-team/brocli": "^0.10.2",
+ "@esbuild-kit/esm-loader": "^2.5.5",
+ "esbuild": "^0.25.4",
+ "tsx": "^4.21.0"
+ },
+ "bin": {
+ "drizzle-kit": "bin.cjs"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+ "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/android-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+ "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/android-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+ "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/android-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+ "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+ "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+ "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+ "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/linux-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+ "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+ "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+ "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+ "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+ "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+ "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+ "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+ "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/linux-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+ "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+ "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+ "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+ "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+ "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/@esbuild/win32-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+ "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/drizzle-kit/node_modules/esbuild": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
+ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.12",
+ "@esbuild/android-arm": "0.25.12",
+ "@esbuild/android-arm64": "0.25.12",
+ "@esbuild/android-x64": "0.25.12",
+ "@esbuild/darwin-arm64": "0.25.12",
+ "@esbuild/darwin-x64": "0.25.12",
+ "@esbuild/freebsd-arm64": "0.25.12",
+ "@esbuild/freebsd-x64": "0.25.12",
+ "@esbuild/linux-arm": "0.25.12",
+ "@esbuild/linux-arm64": "0.25.12",
+ "@esbuild/linux-ia32": "0.25.12",
+ "@esbuild/linux-loong64": "0.25.12",
+ "@esbuild/linux-mips64el": "0.25.12",
+ "@esbuild/linux-ppc64": "0.25.12",
+ "@esbuild/linux-riscv64": "0.25.12",
+ "@esbuild/linux-s390x": "0.25.12",
+ "@esbuild/linux-x64": "0.25.12",
+ "@esbuild/netbsd-arm64": "0.25.12",
+ "@esbuild/netbsd-x64": "0.25.12",
+ "@esbuild/openbsd-arm64": "0.25.12",
+ "@esbuild/openbsd-x64": "0.25.12",
+ "@esbuild/openharmony-arm64": "0.25.12",
+ "@esbuild/sunos-x64": "0.25.12",
+ "@esbuild/win32-arm64": "0.25.12",
+ "@esbuild/win32-ia32": "0.25.12",
+ "@esbuild/win32-x64": "0.25.12"
+ }
+ },
+ "node_modules/drizzle-orm": {
+ "version": "0.45.2",
+ "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.45.2.tgz",
+ "integrity": "sha512-kY0BSaTNYWnoDMVoyY8uxmyHjpJW1geOmBMdSSicKo9CIIWkSxMIj2rkeSR51b8KAPB7m+qysjuHme5nKP+E5Q==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "@aws-sdk/client-rds-data": ">=3",
+ "@cloudflare/workers-types": ">=4",
+ "@electric-sql/pglite": ">=0.2.0",
+ "@libsql/client": ">=0.10.0",
+ "@libsql/client-wasm": ">=0.10.0",
+ "@neondatabase/serverless": ">=0.10.0",
+ "@op-engineering/op-sqlite": ">=2",
+ "@opentelemetry/api": "^1.4.1",
+ "@planetscale/database": ">=1.13",
+ "@prisma/client": "*",
+ "@tidbcloud/serverless": "*",
+ "@types/better-sqlite3": "*",
+ "@types/pg": "*",
+ "@types/sql.js": "*",
+ "@upstash/redis": ">=1.34.7",
+ "@vercel/postgres": ">=0.8.0",
+ "@xata.io/client": "*",
+ "better-sqlite3": ">=7",
+ "bun-types": "*",
+ "expo-sqlite": ">=14.0.0",
+ "gel": ">=2",
+ "knex": "*",
+ "kysely": "*",
+ "mysql2": ">=2",
+ "pg": ">=8",
+ "postgres": ">=3",
+ "sql.js": ">=1",
+ "sqlite3": ">=5"
+ },
+ "peerDependenciesMeta": {
+ "@aws-sdk/client-rds-data": {
+ "optional": true
+ },
+ "@cloudflare/workers-types": {
+ "optional": true
+ },
+ "@electric-sql/pglite": {
+ "optional": true
+ },
+ "@libsql/client": {
+ "optional": true
+ },
+ "@libsql/client-wasm": {
+ "optional": true
+ },
+ "@neondatabase/serverless": {
+ "optional": true
+ },
+ "@op-engineering/op-sqlite": {
+ "optional": true
+ },
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@planetscale/database": {
+ "optional": true
+ },
+ "@prisma/client": {
+ "optional": true
+ },
+ "@tidbcloud/serverless": {
+ "optional": true
+ },
+ "@types/better-sqlite3": {
+ "optional": true
+ },
+ "@types/pg": {
+ "optional": true
+ },
+ "@types/sql.js": {
+ "optional": true
+ },
+ "@upstash/redis": {
+ "optional": true
+ },
+ "@vercel/postgres": {
+ "optional": true
+ },
+ "@xata.io/client": {
+ "optional": true
+ },
+ "better-sqlite3": {
+ "optional": true
+ },
+ "bun-types": {
+ "optional": true
+ },
+ "expo-sqlite": {
+ "optional": true
+ },
+ "gel": {
+ "optional": true
+ },
+ "knex": {
+ "optional": true
+ },
+ "kysely": {
+ "optional": true
+ },
+ "mysql2": {
+ "optional": true
+ },
+ "pg": {
+ "optional": true
+ },
+ "postgres": {
+ "optional": true
+ },
+ "prisma": {
+ "optional": true
+ },
+ "sql.js": {
+ "optional": true
+ },
+ "sqlite3": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/drizzle-zod": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/drizzle-zod/-/drizzle-zod-0.8.3.tgz",
+ "integrity": "sha512-66yVOuvGhKJnTdiqj1/Xaaz9/qzOdRJADpDa68enqS6g3t0kpNkwNYjUuaeXgZfO/UWuIM9HIhSlJ6C5ZraMww==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "drizzle-orm": ">=0.36.0",
+ "zod": "^3.25.0 || ^4.0.0"
+ }
+ },
+ "node_modules/dts-resolver": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/dts-resolver/-/dts-resolver-3.0.0.tgz",
+ "integrity": "sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^22.18.0 || >=24.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sxzz"
+ },
+ "peerDependencies": {
+ "oxc-resolver": ">=11.0.0"
+ },
+ "peerDependenciesMeta": {
+ "oxc-resolver": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/duplexify": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz",
+ "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "end-of-stream": "^1.4.1",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.1.1",
+ "stream-shift": "^1.0.2"
+ }
+ },
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "license": "MIT"
+ },
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/editorconfig": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.7.tgz",
+ "integrity": "sha512-e0GOtq/aTQhVdNyDU9e02+wz9oDDM+SIOQxWME2QRjzRX5yyLAuHDE+0aE8vHb9XRC8XD37eO2u57+F09JqFhw==",
+ "license": "MIT",
+ "dependencies": {
+ "@one-ini/wasm": "0.1.1",
+ "commander": "^10.0.0",
+ "minimatch": "^9.0.1",
+ "semver": "^7.5.3"
+ },
+ "bin": {
+ "editorconfig": "bin/editorconfig"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/editorconfig/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/editorconfig/node_modules/brace-expansion": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz",
+ "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/editorconfig/node_modules/commander": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
+ "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/editorconfig/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",
+ "dependencies": {
+ "brace-expansion": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "license": "MIT"
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/empathic": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.1.tgz",
+ "integrity": "sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
+ "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
+ "license": "MIT",
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/env-paths": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+ "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/environment": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
+ "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/error-ex": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
+ "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-module-lexer": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
+ "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
+ "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "devOptional": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "license": "MIT"
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.39.5",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz",
+ "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.8.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.21.2",
+ "@eslint/config-helpers": "^0.4.2",
+ "@eslint/core": "^0.17.0",
+ "@eslint/eslintrc": "^3.3.6",
+ "@eslint/js": "9.39.5",
+ "@eslint/plugin-kit": "^0.4.1",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "ajv": "^6.14.0",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.4.0",
+ "eslint-visitor-keys": "^4.2.1",
+ "espree": "^10.4.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.5",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/ajv": {
+ "version": "6.15.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz",
+ "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/eslint/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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/eslint/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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eslint/node_modules/brace-expansion": {
+ "version": "1.1.16",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz",
+ "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/eslint/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/eslint/node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eslint/node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/eslint/node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree/node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
+ "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/event-target-shim": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/eventemitter3": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
+ "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/events": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.x"
+ }
+ },
+ "node_modules/events-universal": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz",
+ "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-events": "^2.7.0"
+ }
+ },
+ "node_modules/eventsource": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz",
+ "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==",
+ "license": "MIT",
+ "dependencies": {
+ "eventsource-parser": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/eventsource-parser": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.0.tgz",
+ "integrity": "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "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/execa/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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/expect-type": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz",
+ "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/express": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
+ "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "^2.0.0",
+ "body-parser": "^2.2.1",
+ "content-disposition": "^1.0.0",
+ "content-type": "^1.0.5",
+ "cookie": "^0.7.1",
+ "cookie-signature": "^1.2.1",
+ "debug": "^4.4.0",
+ "depd": "^2.0.0",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "etag": "^1.8.1",
+ "finalhandler": "^2.1.0",
+ "fresh": "^2.0.0",
+ "http-errors": "^2.0.0",
+ "merge-descriptors": "^2.0.0",
+ "mime-types": "^3.0.0",
+ "on-finished": "^2.4.1",
+ "once": "^1.4.0",
+ "parseurl": "^1.3.3",
+ "proxy-addr": "^2.0.7",
+ "qs": "^6.14.0",
+ "range-parser": "^1.2.1",
+ "router": "^2.2.0",
+ "send": "^1.1.0",
+ "serve-static": "^2.2.0",
+ "statuses": "^2.0.1",
+ "type-is": "^2.0.1",
+ "vary": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/express-rate-limit": {
+ "version": "8.6.2",
+ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.6.2.tgz",
+ "integrity": "sha512-YH4ru+eOJxQABscKFfRCy9R7x9QFGdezclVMwwgFFndzS2Xnm0uo6B0ABZsLhcpeptGv2qvuJVWlQr9gQZoC3A==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.3",
+ "ip-address": "^10.2.0"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/express-rate-limit"
+ },
+ "peerDependencies": {
+ "express": ">= 4.11"
+ }
+ },
+ "node_modules/express/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",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/express/node_modules/mime-types": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
+ "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "^1.54.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/farmhash-modern": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/farmhash-modern/-/farmhash-modern-1.1.0.tgz",
+ "integrity": "sha512-6ypT4XfgqJk/F3Yuv4SX26I3doUjt0GTG4a+JgWxXQpxXzTBq8fPUeGHfcYMMDPHJHm3yPOSjaeBwBGAHWXCdA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/fast-copy": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz",
+ "integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==",
+ "license": "MIT"
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "license": "MIT"
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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"
+ },
+ "node_modules/fast-sha256": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz",
+ "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==",
+ "license": "Unlicense"
+ },
+ "node_modules/fast-uri": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.3.tgz",
+ "integrity": "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/fast-xml-builder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.3.0.tgz",
+ "integrity": "sha512-F74cZEdCvuw9P41GAC3rod4X04jjWGM1JPEv/GWSqFTWLsdyMSBMBMlm9Hk3GLBgLBbdBNY8yee0pQh2RBVESQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "path-expression-matcher": "^1.6.2",
+ "xml-naming": "^0.3.0"
+ }
+ },
+ "node_modules/fast-xml-parser": {
+ "version": "5.10.1",
+ "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.10.1.tgz",
+ "integrity": "sha512-IEMIf7298kXuZSRFoGfMYrl7is8LpavODgbNz1cwIudv7KwVFnuU+UsMporfq6PD6aXSlawZlARiA3UywCTfMw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@nodable/entities": "^3.0.0",
+ "fast-xml-builder": "^1.2.0",
+ "is-unsafe": "^2.0.0",
+ "path-expression-matcher": "^1.6.2",
+ "strnum": "^2.4.1",
+ "xml-naming": "^0.3.0"
+ },
+ "bin": {
+ "fxparser": "src/cli/cli.js"
+ }
+ },
+ "node_modules/faye-websocket": {
+ "version": "0.11.4",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
+ "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "websocket-driver": ">=0.5.1"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/finalhandler": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz",
+ "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.0",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "on-finished": "^2.4.1",
+ "parseurl": "^1.3.3",
+ "statuses": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 18.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "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==",
+ "dev": true,
+ "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/firebase-admin": {
+ "version": "12.7.0",
+ "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-12.7.0.tgz",
+ "integrity": "sha512-raFIrOyTqREbyXsNkSHyciQLfv8AUZazehPaQS1lZBSCDYW74FYXU0nQZa3qHI4K+hawohlDbywZ4+qce9YNxA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@fastify/busboy": "^3.0.0",
+ "@firebase/database-compat": "1.0.8",
+ "@firebase/database-types": "1.0.5",
+ "@types/node": "^22.0.1",
+ "farmhash-modern": "^1.1.0",
+ "jsonwebtoken": "^9.0.0",
+ "jwks-rsa": "^3.1.0",
+ "node-forge": "^1.3.1",
+ "uuid": "^10.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "optionalDependencies": {
+ "@google-cloud/firestore": "^7.7.0",
+ "@google-cloud/storage": "^7.7.0"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
+ "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/form-data": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.6.tgz",
+ "integrity": "sha512-Ogz/E85h9tlfJzpI6TuFpGcHZFhLrb9Gw8wq9v40CxSCPnv7ahKr6Xgtkn0KYCDQJ8DNn5VoMO8EXr9V5PadyA==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.4",
+ "mime-types": "^2.1.35",
+ "safe-buffer": "^5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.12"
+ }
+ },
+ "node_modules/formidable": {
+ "version": "3.5.4",
+ "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz",
+ "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@paralleldrive/cuid2": "^2.2.2",
+ "dezalgo": "^1.0.4",
+ "once": "^1.4.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "url": "https://ko-fi.com/tunnckoCore/commissions"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
+ "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/fs-constants": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/functional-red-black-tree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+ "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/gaxios": {
+ "version": "6.7.1",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz",
+ "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "extend": "^3.0.2",
+ "https-proxy-agent": "^7.0.1",
+ "is-stream": "^2.0.0",
+ "node-fetch": "^2.6.9",
+ "uuid": "^9.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/gaxios/node_modules/uuid": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+ "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/gcp-metadata": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz",
+ "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "gaxios": "^6.1.1",
+ "google-logging-utils": "^0.0.2",
+ "json-bigint": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "devOptional": true,
+ "license": "ISC",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-east-asian-width": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz",
+ "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-port": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz",
+ "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz",
+ "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/git-raw-commits": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz",
+ "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==",
+ "deprecated": "Deprecated and no longer maintained. Use @conventional-changelog/git-client instead.",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dargs": "^8.0.0",
+ "meow": "^12.0.1",
+ "split2": "^4.0.0"
+ },
+ "bin": {
+ "git-raw-commits": "cli.mjs"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/glob": {
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+ "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
+ "license": "ISC",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/glob/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/glob/node_modules/brace-expansion": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz",
+ "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/glob/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",
+ "dependencies": {
+ "brace-expansion": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/global-directory": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz",
+ "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ini": "4.1.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/google-auth-library": {
+ "version": "9.15.1",
+ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz",
+ "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "base64-js": "^1.3.0",
+ "ecdsa-sig-formatter": "^1.0.11",
+ "gaxios": "^6.1.1",
+ "gcp-metadata": "^6.1.0",
+ "gtoken": "^7.0.0",
+ "jws": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/google-gax": {
+ "version": "4.6.1",
+ "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.6.1.tgz",
+ "integrity": "sha512-V6eky/xz2mcKfAd1Ioxyd6nmA61gao3n01C+YeuIwu3vzM9EDR6wcVzMSIbLMDXWeoi9SHYctXuKYC5uJUT3eQ==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@grpc/grpc-js": "^1.10.9",
+ "@grpc/proto-loader": "^0.7.13",
+ "@types/long": "^4.0.0",
+ "abort-controller": "^3.0.0",
+ "duplexify": "^4.0.0",
+ "google-auth-library": "^9.3.0",
+ "node-fetch": "^2.7.0",
+ "object-hash": "^3.0.0",
+ "proto3-json-serializer": "^2.0.2",
+ "protobufjs": "^7.3.2",
+ "retry-request": "^7.0.0",
+ "uuid": "^9.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/google-gax/node_modules/uuid": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+ "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/google-logging-utils": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz",
+ "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/gtoken": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz",
+ "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "gaxios": "^6.0.0",
+ "jws": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
+ "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/help-me": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz",
+ "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==",
+ "license": "MIT"
+ },
+ "node_modules/hono": {
+ "version": "4.13.2",
+ "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.2.tgz",
+ "integrity": "sha512-JydRilDRkYBQMt9qR9U92mXxmbGqsqSn/IKOrh4e7/gEbn+0zSr8igTu0obwJoNGN4sez28DIql7FBHWydoJpA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.9.0"
+ }
+ },
+ "node_modules/hookable": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/hookable/-/hookable-6.1.1.tgz",
+ "integrity": "sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/html-entities": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz",
+ "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/mdevils"
+ },
+ {
+ "type": "patreon",
+ "url": "https://patreon.com/mdevils"
+ }
+ ],
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/html-escaper": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
+ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/html-to-text": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz",
+ "integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==",
+ "license": "MIT",
+ "dependencies": {
+ "@selderee/plugin-htmlparser2": "^0.11.0",
+ "deepmerge": "^4.3.1",
+ "dom-serializer": "^2.0.0",
+ "htmlparser2": "^8.0.2",
+ "selderee": "^0.11.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/htmlparser2": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
+ "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
+ "funding": [
+ "https://github.com/fb55/htmlparser2?sponsor=1",
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.0.1",
+ "entities": "^4.4.0"
+ }
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+ "license": "MIT",
+ "dependencies": {
+ "depd": "~2.0.0",
+ "inherits": "~2.0.4",
+ "setprototypeof": "~1.2.0",
+ "statuses": "~2.0.2",
+ "toidentifier": "~1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/http-parser-js": {
+ "version": "0.5.10",
+ "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz",
+ "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==",
+ "license": "MIT"
+ },
+ "node_modules/http-proxy-agent": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
+ "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@tootallnate/once": "2",
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/http-proxy-agent/node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "agent-base": "^7.1.2",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=16.17.0"
+ }
+ },
+ "node_modules/husky": {
+ "version": "9.1.7",
+ "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
+ "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "husky": "bin.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/typicode"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz",
+ "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "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"
+ },
+ "node_modules/ignore": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz",
+ "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/import-fresh/node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/import-meta-resolve": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz",
+ "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/import-without-cache": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/import-without-cache/-/import-without-cache-0.4.0.tgz",
+ "integrity": "sha512-NkJQA7oZ4YHQhd2+H3BoRFKF3d/XNsiKpHZCQEMH9pDX27hQQLsTyOocyRgaIVtf8gHX3Nt3LPkR4e5EdtPAGQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^22.18.0 || >=24.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sxzz"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC"
+ },
+ "node_modules/ini": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz",
+ "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/ioredis": {
+ "version": "5.11.1",
+ "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.11.1.tgz",
+ "integrity": "sha512-ehuGcf94bQXhfagULNXrJdfnWO38v070jxSx/qE87Kjzmu2fU7ro5EFAb+OPituLqgfyuQaym5DlrNydW2sJ9A==",
+ "license": "MIT",
+ "dependencies": {
+ "@ioredis/commands": "1.10.0",
+ "cluster-key-slot": "1.1.1",
+ "debug": "4.4.3",
+ "denque": "2.1.0",
+ "redis-errors": "1.2.0",
+ "redis-parser": "3.0.0",
+ "standard-as-callback": "2.1.0"
+ },
+ "engines": {
+ "node": ">=12.22.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ioredis"
+ }
+ },
+ "node_modules/ip-address": {
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.5.0.tgz",
+ "integrity": "sha512-R5SnVLJmgYYvf2F2ZgwSBnelz5G4q5AxIC277GDfUaNbrZKNANcBC7RHqYYePlszf4kBolVkJauG0ZjHHFh55g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
+ "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-obj": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
+ "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-promise": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
+ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
+ "license": "MIT"
+ },
+ "node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "devOptional": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-text-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz",
+ "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "text-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-unsafe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-unsafe/-/is-unsafe-2.0.0.tgz",
+ "integrity": "sha512-2LdV822R+wmI86unXA93WCFpL6g+av8ynWk0nrHyJqGop5VoocYsSLFgN8jrfalT6iGeLNM4KXuVSsULP53kEA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/istanbul-lib-coverage": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
+ "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-report": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
+ "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "istanbul-lib-coverage": "^3.0.0",
+ "make-dir": "^4.0.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/istanbul-lib-source-maps": {
+ "version": "5.0.6",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz",
+ "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@jridgewell/trace-mapping": "^0.3.23",
+ "debug": "^4.1.1",
+ "istanbul-lib-coverage": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/istanbul-reports": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz",
+ "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "html-escaper": "^2.0.0",
+ "istanbul-lib-report": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/jackspeak": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
+ "node_modules/jiti": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
+ "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
+ "node_modules/jose": {
+ "version": "4.15.9",
+ "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz",
+ "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/panva"
+ }
+ },
+ "node_modules/joycon": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz",
+ "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/js-beautify": {
+ "version": "1.15.4",
+ "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz",
+ "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==",
+ "license": "MIT",
+ "dependencies": {
+ "config-chain": "^1.1.13",
+ "editorconfig": "^1.0.4",
+ "glob": "^10.4.2",
+ "js-cookie": "^3.0.5",
+ "nopt": "^7.2.1"
+ },
+ "bin": {
+ "css-beautify": "js/bin/css-beautify.js",
+ "html-beautify": "js/bin/html-beautify.js",
+ "js-beautify": "js/bin/js-beautify.js"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/js-cookie": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.8.tgz",
+ "integrity": "sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==",
+ "license": "MIT"
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/js-yaml": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
+ "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/json-bigint": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
+ "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "bignumber.js": "^9.0.0"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
+ "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
+ "license": "(AFL-2.1 OR BSD-3-Clause)"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "license": "MIT"
+ },
+ "node_modules/json-schema-typed": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz",
+ "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==",
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/jsonparse": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+ "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==",
+ "dev": true,
+ "engines": [
+ "node >= 0.2.0"
+ ],
+ "license": "MIT"
+ },
+ "node_modules/JSONStream": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
+ "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
+ "dev": true,
+ "license": "(MIT OR Apache-2.0)",
+ "dependencies": {
+ "jsonparse": "^1.2.0",
+ "through": ">=2.2.7 <3"
+ },
+ "bin": {
+ "JSONStream": "bin.js"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/jsonwebtoken": {
+ "version": "9.0.3",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
+ "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==",
+ "license": "MIT",
+ "dependencies": {
+ "jws": "^4.0.1",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=12",
+ "npm": ">=6"
+ }
+ },
+ "node_modules/jwa": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
+ "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer-equal-constant-time": "^1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jwks-rsa": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.2.2.tgz",
+ "integrity": "sha512-BqTyEDV+lS8F2trk3A+qJnxV5Q9EqKCBJOPti3W97r7qTympCZjb7h2X6f2kc+0K3rsSTY1/6YG2eaXKoj497w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/jsonwebtoken": "^9.0.4",
+ "debug": "^4.3.4",
+ "jose": "^4.15.4",
+ "limiter": "^1.1.5",
+ "lru-memoizer": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/jws": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
+ "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
+ "license": "MIT",
+ "dependencies": {
+ "jwa": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/lazystream": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
+ "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "readable-stream": "^2.0.5"
+ },
+ "engines": {
+ "node": ">= 0.6.3"
+ }
+ },
+ "node_modules/lazystream/node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/lazystream/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lazystream/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/leac": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz",
+ "integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://ko-fi.com/killymxi"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antonk52"
+ }
+ },
+ "node_modules/limiter": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz",
+ "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA=="
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lint-staged": {
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.5.2.tgz",
+ "integrity": "sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "^5.4.1",
+ "commander": "^13.1.0",
+ "debug": "^4.4.0",
+ "execa": "^8.0.1",
+ "lilconfig": "^3.1.3",
+ "listr2": "^8.2.5",
+ "micromatch": "^4.0.8",
+ "pidtree": "^0.6.0",
+ "string-argv": "^0.3.2",
+ "yaml": "^2.7.0"
+ },
+ "bin": {
+ "lint-staged": "bin/lint-staged.js"
+ },
+ "engines": {
+ "node": ">=18.12.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/lint-staged"
+ }
+ },
+ "node_modules/listr2": {
+ "version": "8.3.3",
+ "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.3.3.tgz",
+ "integrity": "sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cli-truncate": "^4.0.0",
+ "colorette": "^2.0.20",
+ "eventemitter3": "^5.0.1",
+ "log-update": "^6.1.0",
+ "rfdc": "^1.4.1",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=18.0.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==",
+ "dev": true,
+ "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": {
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
+ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.camelcase": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+ "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
+ "devOptional": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.clonedeep": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+ "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.kebabcase": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz",
+ "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.mergewith": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
+ "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.snakecase": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
+ "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.startcase": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz",
+ "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.uniq": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+ "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.upperfirst": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz",
+ "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/log-update": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz",
+ "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-escapes": "^7.0.0",
+ "cli-cursor": "^5.0.0",
+ "slice-ansi": "^7.1.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/is-fullwidth-code-point": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz",
+ "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-east-asian-width": "^1.3.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/slice-ansi": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz",
+ "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "is-fullwidth-code-point": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/long": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
+ "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
+ "devOptional": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/loupe": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz",
+ "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/lru-memoizer": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz",
+ "integrity": "sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==",
+ "license": "MIT",
+ "dependencies": {
+ "lodash.clonedeep": "^4.5.0",
+ "lru-cache": "6.0.0"
+ }
+ },
+ "node_modules/magic-string": {
+ "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/magicast": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz",
+ "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.25.4",
+ "@babel/types": "^7.25.4",
+ "source-map-js": "^1.2.0"
+ }
+ },
+ "node_modules/make-dir": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
+ "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.5.3"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/media-typer": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz",
+ "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/meow": {
+ "version": "12.1.1",
+ "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz",
+ "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
+ "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/micromatch/node_modules/picomatch": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/mime": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
+ "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
+ "license": "MIT",
+ "optional": true,
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "devOptional": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.5"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
+ "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/mkdirp": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
+ "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "mkdirp": "dist/cjs/src/bin.js"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/mkdirp-classic": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
+ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/nan": {
+ "version": "2.28.0",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.28.0.tgz",
+ "integrity": "sha512-fTsDz99OTq2sVePhGdp4qQhggZFtKr64ZNVyVajRKtMOkJxYekplBh577PiJB12v/D3s2E5cGtOI45LWp6rnLQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.16",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
+ "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/negotiator": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
+ "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/node-fetch": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/node-forge": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz",
+ "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==",
+ "license": "(BSD-3-Clause OR GPL-2.0)",
+ "engines": {
+ "node": ">= 6.13.0"
+ }
+ },
+ "node_modules/nopt": {
+ "version": "7.2.1",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz",
+ "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==",
+ "license": "ISC",
+ "dependencies": {
+ "abbrev": "^2.0.0"
+ },
+ "bin": {
+ "nopt": "bin/nopt.js"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/notifkit": {
+ "resolved": "",
+ "link": true
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "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/npm-run-path/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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-hash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
+ "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/obug": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz",
+ "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==",
+ "dev": true,
+ "funding": [
+ "https://github.com/sponsors/sxzz",
+ "https://opencollective.com/debug"
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20.0"
+ }
+ },
+ "node_modules/on-exit-leak-free": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz",
+ "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-fn": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "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==",
+ "dev": true,
+ "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-locate/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==",
+ "dev": true,
+ "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/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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "license": "BlueOak-1.0.0"
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parseley": {
+ "version": "0.12.1",
+ "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz",
+ "integrity": "sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==",
+ "license": "MIT",
+ "dependencies": {
+ "leac": "^0.6.0",
+ "peberminta": "^0.9.0"
+ },
+ "funding": {
+ "url": "https://ko-fi.com/killymxi"
+ }
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ }
+ },
+ "node_modules/path-expression-matcher": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.2.tgz",
+ "integrity": "sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14.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-scurry": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/path-scurry/node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "license": "ISC"
+ },
+ "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",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/pathval": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz",
+ "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.16"
+ }
+ },
+ "node_modules/peberminta": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz",
+ "integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://ko-fi.com/killymxi"
+ }
+ },
+ "node_modules/pg": {
+ "version": "8.22.0",
+ "resolved": "https://registry.npmjs.org/pg/-/pg-8.22.0.tgz",
+ "integrity": "sha512-8wih1vVIBMxoUM2oB4soJsD9tDnDpLv4OXBJ+EJzFsvycD+lfyIreC2gGHq78f8jbLLt+bvlPTFdFZfJkOuzAA==",
+ "license": "MIT",
+ "dependencies": {
+ "pg-connection-string": "^2.14.0",
+ "pg-pool": "^3.14.0",
+ "pg-protocol": "^1.15.0",
+ "pg-types": "2.2.0",
+ "pgpass": "1.0.5"
+ },
+ "engines": {
+ "node": ">= 16.0.0"
+ },
+ "optionalDependencies": {
+ "pg-cloudflare": "^1.4.0"
+ },
+ "peerDependencies": {
+ "pg-native": ">=3.0.1"
+ },
+ "peerDependenciesMeta": {
+ "pg-native": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/pg-cloudflare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz",
+ "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/pg-connection-string": {
+ "version": "2.14.0",
+ "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.14.0.tgz",
+ "integrity": "sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg==",
+ "license": "MIT"
+ },
+ "node_modules/pg-int8": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
+ "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/pg-pool": {
+ "version": "3.14.0",
+ "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz",
+ "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "pg": ">=8.0"
+ }
+ },
+ "node_modules/pg-protocol": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.15.0.tgz",
+ "integrity": "sha512-cq9sECI5s0+uPUXjbz8ioyPJni6RzsRib0US67i5IoTZKw8fNeYlVE7u8F4dG7vEJJtc5wdD1K189lCCUwqWTQ==",
+ "license": "MIT"
+ },
+ "node_modules/pg-types": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
+ "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
+ "license": "MIT",
+ "dependencies": {
+ "pg-int8": "1.0.1",
+ "postgres-array": "~2.0.0",
+ "postgres-bytea": "~1.0.0",
+ "postgres-date": "~1.0.4",
+ "postgres-interval": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/pgpass": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
+ "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
+ "license": "MIT",
+ "dependencies": {
+ "split2": "^4.1.0"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
+ "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pidtree": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.1.tgz",
+ "integrity": "sha512-e0F9AOF1JMrCfBsyJOwU9lNvQ0WtXTq0j/4jk0BQ5JSI9VAybPXmDpPRw/2FQ3e5d3ZFN1mLh7jW99m/jjaptw==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "pidtree": "bin/pidtree.js"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/pino": {
+ "version": "9.14.0",
+ "resolved": "https://registry.npmjs.org/pino/-/pino-9.14.0.tgz",
+ "integrity": "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==",
+ "license": "MIT",
+ "dependencies": {
+ "@pinojs/redact": "^0.4.0",
+ "atomic-sleep": "^1.0.0",
+ "on-exit-leak-free": "^2.1.0",
+ "pino-abstract-transport": "^2.0.0",
+ "pino-std-serializers": "^7.0.0",
+ "process-warning": "^5.0.0",
+ "quick-format-unescaped": "^4.0.3",
+ "real-require": "^0.2.0",
+ "safe-stable-stringify": "^2.3.1",
+ "sonic-boom": "^4.0.1",
+ "thread-stream": "^3.0.0"
+ },
+ "bin": {
+ "pino": "bin.js"
+ }
+ },
+ "node_modules/pino-abstract-transport": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz",
+ "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==",
+ "license": "MIT",
+ "dependencies": {
+ "split2": "^4.0.0"
+ }
+ },
+ "node_modules/pino-pretty": {
+ "version": "11.3.0",
+ "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-11.3.0.tgz",
+ "integrity": "sha512-oXwn7ICywaZPHmu3epHGU2oJX4nPmKvHvB/bwrJHlGcbEWaVcotkpyVHMKLKmiVryWYByNp0jpgAcXpFJDXJzA==",
+ "license": "MIT",
+ "dependencies": {
+ "colorette": "^2.0.7",
+ "dateformat": "^4.6.3",
+ "fast-copy": "^3.0.2",
+ "fast-safe-stringify": "^2.1.1",
+ "help-me": "^5.0.0",
+ "joycon": "^3.1.1",
+ "minimist": "^1.2.6",
+ "on-exit-leak-free": "^2.1.0",
+ "pino-abstract-transport": "^2.0.0",
+ "pump": "^3.0.0",
+ "readable-stream": "^4.0.0",
+ "secure-json-parse": "^2.4.0",
+ "sonic-boom": "^4.0.1",
+ "strip-json-comments": "^3.1.1"
+ },
+ "bin": {
+ "pino-pretty": "bin.js"
+ }
+ },
+ "node_modules/pino-pretty/node_modules/readable-stream": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
+ "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
+ "license": "MIT",
+ "dependencies": {
+ "abort-controller": "^3.0.0",
+ "buffer": "^6.0.3",
+ "events": "^3.3.0",
+ "process": "^0.11.10",
+ "string_decoder": "^1.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/pino-std-serializers": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz",
+ "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==",
+ "license": "MIT"
+ },
+ "node_modules/pkce-challenge": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz",
+ "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.19",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.19.tgz",
+ "integrity": "sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.12",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postgres": {
+ "version": "3.4.9",
+ "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.9.tgz",
+ "integrity": "sha512-GD3qdB0x1z9xgFI6cdRD6xu2Sp2WCOEoe3mtnyB5Ee0XrrL5Pe+e4CCnJrRMnL1zYtRDZmQQVbvOttLnKDLnaw==",
+ "license": "Unlicense",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://github.com/sponsors/porsager"
+ }
+ },
+ "node_modules/postgres-array": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
+ "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postgres-bytea": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz",
+ "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/postgres-date": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
+ "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/postgres-interval": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
+ "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "xtend": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prettier": {
+ "version": "3.9.5",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.5.tgz",
+ "integrity": "sha512-/FVl766LpUfB5vXgCYOYa0MeV/441Ia99AeICQIQFTY/Nw0roZwULcXpku5i1/m5kt/baz+s4Zogspd839HSMg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/process-warning": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz",
+ "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/prom-client": {
+ "version": "15.1.3",
+ "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-15.1.3.tgz",
+ "integrity": "sha512-6ZiOBfCywsD4k1BN9IX0uZhF+tJkV8q8llP64G5Hajs4JOeVLPCwpPVcpXy3BwYiUGgyJzsJJQeOIv7+hDSq8g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@opentelemetry/api": "^1.4.0",
+ "tdigest": "^0.1.1"
+ },
+ "engines": {
+ "node": "^16 || ^18 || >=20"
+ }
+ },
+ "node_modules/proper-lockfile": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz",
+ "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "retry": "^0.12.0",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "node_modules/proper-lockfile/node_modules/retry": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
+ "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/proper-lockfile/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==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/properties-reader": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/properties-reader/-/properties-reader-3.0.1.tgz",
+ "integrity": "sha512-WPn+h9RGEExOKdu4bsF4HksG/uzd3cFq3MFtq8PsFeExPse5Ha/VOjQNyHhjboBFwGXGev6muJYTSPAOkROq2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@kwsites/file-exists": "^1.1.1",
+ "mkdirp": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/steveukx/properties?sponsor=1"
+ }
+ },
+ "node_modules/proto-list": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
+ "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==",
+ "license": "ISC"
+ },
+ "node_modules/proto3-json-serializer": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz",
+ "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "protobufjs": "^7.2.5"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/protobufjs": {
+ "version": "7.6.5",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.5.tgz",
+ "integrity": "sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==",
+ "devOptional": true,
+ "hasInstallScript": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.2",
+ "@protobufjs/base64": "^1.1.2",
+ "@protobufjs/codegen": "^2.0.5",
+ "@protobufjs/eventemitter": "^1.1.1",
+ "@protobufjs/fetch": "^1.1.1",
+ "@protobufjs/float": "^1.0.2",
+ "@protobufjs/path": "^1.1.2",
+ "@protobufjs/pool": "^1.1.0",
+ "@protobufjs/utf8": "^1.1.1",
+ "@types/node": ">=13.7.0",
+ "long": "^5.3.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "license": "MIT",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/pump": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
+ "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
+ "license": "MIT",
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.15.3",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz",
+ "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "es-define-property": "^1.0.1",
+ "side-channel": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/quansync": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/quansync/-/quansync-1.0.0.tgz",
+ "integrity": "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/antfu"
+ },
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/sxzz"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/quick-format-unescaped": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
+ "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==",
+ "license": "MIT"
+ },
+ "node_modules/range-parser": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz",
+ "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
+ "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.7.0",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.2.7",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz",
+ "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.2.7",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz",
+ "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.7"
+ }
+ },
+ "node_modules/react-promise-suspense": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/react-promise-suspense/-/react-promise-suspense-0.3.4.tgz",
+ "integrity": "sha512-I42jl7L3Ze6kZaq+7zXWSunBa3b1on5yfvUW6Eo/3fFOj6dZ5Bqmcd264nJbTK/gn1HjjILAjSwnZbV4RpSaNQ==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^2.0.1"
+ }
+ },
+ "node_modules/react-promise-suspense/node_modules/fast-deep-equal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+ "integrity": "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==",
+ "license": "MIT"
+ },
+ "node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/readdir-glob": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz",
+ "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "minimatch": "^5.1.0"
+ }
+ },
+ "node_modules/readdir-glob/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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/readdir-glob/node_modules/brace-expansion": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz",
+ "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/readdir-glob/node_modules/minimatch": {
+ "version": "5.1.9",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz",
+ "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/real-require": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",
+ "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12.13.0"
+ }
+ },
+ "node_modules/redis-errors": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
+ "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/redis-parser": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
+ "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
+ "license": "MIT",
+ "dependencies": {
+ "redis-errors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "devOptional": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resend": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/resend/-/resend-3.5.0.tgz",
+ "integrity": "sha512-bKu4LhXSecP6krvhfDzyDESApYdNfjirD5kykkT1xO0Cj9TKSiGh5Void4pGTs3Am+inSnp4dg0B5XzdwHBJOQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-email/render": "0.0.16"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
+ "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==",
+ "dev": true,
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-function": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/retry": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
+ "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/retry-request": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz",
+ "integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@types/request": "^2.48.8",
+ "extend": "^3.0.2",
+ "teeny-request": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/rfdc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/rolldown": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.1.tgz",
+ "integrity": "sha512-4FKJhg8d3OiyQOA6Q1Q0hoFFpW9/OoX+VsHzpECsdsIZoOArrAK90gl59YK/Z+gnDel45bgJZK03ozH/9bCqEw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@oxc-project/types": "=0.142.0",
+ "@rolldown/pluginutils": "^1.0.0"
+ },
+ "bin": {
+ "rolldown": "bin/cli.mjs"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "optionalDependencies": {
+ "@rolldown/binding-android-arm64": "1.2.1",
+ "@rolldown/binding-darwin-arm64": "1.2.1",
+ "@rolldown/binding-darwin-x64": "1.2.1",
+ "@rolldown/binding-freebsd-x64": "1.2.1",
+ "@rolldown/binding-linux-arm-gnueabihf": "1.2.1",
+ "@rolldown/binding-linux-arm64-gnu": "1.2.1",
+ "@rolldown/binding-linux-arm64-musl": "1.2.1",
+ "@rolldown/binding-linux-ppc64-gnu": "1.2.1",
+ "@rolldown/binding-linux-s390x-gnu": "1.2.1",
+ "@rolldown/binding-linux-x64-gnu": "1.2.1",
+ "@rolldown/binding-linux-x64-musl": "1.2.1",
+ "@rolldown/binding-openharmony-arm64": "1.2.1",
+ "@rolldown/binding-wasm32-wasi": "1.2.1",
+ "@rolldown/binding-win32-arm64-msvc": "1.2.1",
+ "@rolldown/binding-win32-x64-msvc": "1.2.1"
+ }
+ },
+ "node_modules/rolldown-plugin-dts": {
+ "version": "0.27.14",
+ "resolved": "https://registry.npmjs.org/rolldown-plugin-dts/-/rolldown-plugin-dts-0.27.14.tgz",
+ "integrity": "sha512-ZvuDDwoIpRK9RPxDXratCpklFO9QZZWndf/sd0VBFb4LEj0jj07UcHK9OCh7V4XiFz2Z89ziyBC2K6tJiDjrbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dts-resolver": "^3.0.0",
+ "get-tsconfig": "5.0.0-beta.5",
+ "obug": "^2.1.4",
+ "yuku-ast": "^0.8.0",
+ "yuku-codegen": "^0.8.0",
+ "yuku-parser": "^0.8.0"
+ },
+ "engines": {
+ "node": "^22.18.0 || >=24.11.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sxzz"
+ },
+ "peerDependencies": {
+ "@typescript/native-preview": "*",
+ "@volar/typescript": "~2.4.0",
+ "rolldown": "^1.0.0",
+ "typescript": "^5.0.0 || ^6.0.0 || ~7.0.0",
+ "vue-tsc": "~3.2.0 || ~3.3.0"
+ },
+ "peerDependenciesMeta": {
+ "@typescript/native-preview": {
+ "optional": true
+ },
+ "@volar/typescript": {
+ "optional": true
+ },
+ "typescript": {
+ "optional": true
+ },
+ "vue-tsc": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/rolldown-plugin-dts/node_modules/get-tsconfig": {
+ "version": "5.0.0-beta.5",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-5.0.0-beta.5.tgz",
+ "integrity": "sha512-/6gFNr0N04nob252sTQxyFLi3eKFRqIg1I87YcqAMT1i6SQrSF6KujUEQrtrjMV0H/eejTCltLdDSTEMzHbnsQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=20.20.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz",
+ "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.9"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.62.2",
+ "@rollup/rollup-android-arm64": "4.62.2",
+ "@rollup/rollup-darwin-arm64": "4.62.2",
+ "@rollup/rollup-darwin-x64": "4.62.2",
+ "@rollup/rollup-freebsd-arm64": "4.62.2",
+ "@rollup/rollup-freebsd-x64": "4.62.2",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.62.2",
+ "@rollup/rollup-linux-arm-musleabihf": "4.62.2",
+ "@rollup/rollup-linux-arm64-gnu": "4.62.2",
+ "@rollup/rollup-linux-arm64-musl": "4.62.2",
+ "@rollup/rollup-linux-loong64-gnu": "4.62.2",
+ "@rollup/rollup-linux-loong64-musl": "4.62.2",
+ "@rollup/rollup-linux-ppc64-gnu": "4.62.2",
+ "@rollup/rollup-linux-ppc64-musl": "4.62.2",
+ "@rollup/rollup-linux-riscv64-gnu": "4.62.2",
+ "@rollup/rollup-linux-riscv64-musl": "4.62.2",
+ "@rollup/rollup-linux-s390x-gnu": "4.62.2",
+ "@rollup/rollup-linux-x64-gnu": "4.62.2",
+ "@rollup/rollup-linux-x64-musl": "4.62.2",
+ "@rollup/rollup-openbsd-x64": "4.62.2",
+ "@rollup/rollup-openharmony-arm64": "4.62.2",
+ "@rollup/rollup-win32-arm64-msvc": "4.62.2",
+ "@rollup/rollup-win32-ia32-msvc": "4.62.2",
+ "@rollup/rollup-win32-x64-gnu": "4.62.2",
+ "@rollup/rollup-win32-x64-msvc": "4.62.2",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/router": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
+ "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.0",
+ "depd": "^2.0.0",
+ "is-promise": "^4.0.0",
+ "parseurl": "^1.3.3",
+ "path-to-regexp": "^8.0.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "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"
+ },
+ "node_modules/safe-stable-stringify": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
+ "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
+ },
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
+ },
+ "node_modules/secure-json-parse": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz",
+ "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/selderee": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz",
+ "integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==",
+ "license": "MIT",
+ "dependencies": {
+ "parseley": "^0.12.0"
+ },
+ "funding": {
+ "url": "https://ko-fi.com/killymxi"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.8.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/send": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz",
+ "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.3",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "etag": "^1.8.1",
+ "fresh": "^2.0.0",
+ "http-errors": "^2.0.1",
+ "mime-types": "^3.0.2",
+ "ms": "^2.1.3",
+ "on-finished": "^2.4.1",
+ "range-parser": "^1.2.1",
+ "statuses": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/send/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",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/send/node_modules/mime-types": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
+ "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "^1.54.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/serve-static": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz",
+ "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==",
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "parseurl": "^1.3.3",
+ "send": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "license": "ISC"
+ },
+ "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/side-channel": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
+ "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4",
+ "side-channel-list": "^1.0.1",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
+ "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/siginfo": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
+ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "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/slice-ansi": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
+ "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.0.0",
+ "is-fullwidth-code-point": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/sonic-boom": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz",
+ "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==",
+ "license": "MIT",
+ "dependencies": {
+ "atomic-sleep": "^1.0.0"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/source-map-support/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/split-ca": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz",
+ "integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/split2": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
+ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">= 10.x"
+ }
+ },
+ "node_modules/ssh-remote-port-forward": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/ssh-remote-port-forward/-/ssh-remote-port-forward-1.0.4.tgz",
+ "integrity": "sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/ssh2": "^0.5.48",
+ "ssh2": "^1.4.0"
+ }
+ },
+ "node_modules/ssh-remote-port-forward/node_modules/@types/ssh2": {
+ "version": "0.5.52",
+ "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-0.5.52.tgz",
+ "integrity": "sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*",
+ "@types/ssh2-streams": "*"
+ }
+ },
+ "node_modules/ssh2": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.17.0.tgz",
+ "integrity": "sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==",
+ "dev": true,
+ "hasInstallScript": true,
+ "dependencies": {
+ "asn1": "^0.2.6",
+ "bcrypt-pbkdf": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=10.16.0"
+ },
+ "optionalDependencies": {
+ "cpu-features": "~0.0.10",
+ "nan": "^2.23.0"
+ }
+ },
+ "node_modules/stackback": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
+ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/standard-as-callback": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
+ "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==",
+ "license": "MIT"
+ },
+ "node_modules/standardwebhooks": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.0.0.tgz",
+ "integrity": "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==",
+ "license": "MIT",
+ "dependencies": {
+ "@stablelib/base64": "^1.0.0",
+ "fast-sha256": "^1.3.0"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/std-env": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz",
+ "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/stream-events": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz",
+ "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "stubs": "^3.0.0"
+ }
+ },
+ "node_modules/stream-shift": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
+ "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/streamx": {
+ "version": "2.28.0",
+ "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz",
+ "integrity": "sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "events-universal": "^1.0.0",
+ "fast-fifo": "^1.3.2",
+ "text-decoder": "^1.1.0"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/string-argv": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
+ "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6.19"
+ }
+ },
+ "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==",
+ "dev": true,
+ "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/string-width-cjs": {
+ "name": "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-cjs/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/string-width-cjs/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/string-width-cjs/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/string-width-cjs/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-cjs": {
+ "name": "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-cjs/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/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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/strnum": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.1.tgz",
+ "integrity": "sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "anynum": "^1.0.1"
+ }
+ },
+ "node_modules/stubs": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz",
+ "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/superagent": {
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.3.0.tgz",
+ "integrity": "sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "component-emitter": "^1.3.1",
+ "cookiejar": "^2.1.4",
+ "debug": "^4.3.7",
+ "fast-safe-stringify": "^2.1.1",
+ "form-data": "^4.0.5",
+ "formidable": "^3.5.4",
+ "methods": "^1.1.2",
+ "mime": "2.6.0",
+ "qs": "^6.14.1"
+ },
+ "engines": {
+ "node": ">=14.18.0"
+ }
+ },
+ "node_modules/superagent/node_modules/form-data": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz",
+ "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.4",
+ "mime-types": "^2.1.35"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/superagent/node_modules/mime": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+ "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/supertest": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.2.2.tgz",
+ "integrity": "sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cookie-signature": "^1.2.2",
+ "methods": "^1.1.2",
+ "superagent": "^10.3.0"
+ },
+ "engines": {
+ "node": ">=14.18.0"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/svix": {
+ "version": "1.99.1",
+ "resolved": "https://registry.npmjs.org/svix/-/svix-1.99.1.tgz",
+ "integrity": "sha512-JfpvbAg5iT5NagDAfOq3e6Ci6NbOMbMm6C3DnfDBB60m2JDK+Z2hXPE9XNAmutb53DCdPoih1V70IbklZnX09A==",
+ "license": "MIT",
+ "dependencies": {
+ "standardwebhooks": "1.0.0"
+ }
+ },
+ "node_modules/tar-fs": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.3.tgz",
+ "integrity": "sha512-/hU4AXnIdZu+Gvl1pk0oI5f5HxWsCJRtY2aFaJdk9VvyL48DWU6iU5WAIPG+wIi1YvWA6eTJvIviP/tMAZZNwQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pump": "^3.0.0",
+ "tar-stream": "^3.1.5"
+ },
+ "optionalDependencies": {
+ "bare-fs": "^4.0.1",
+ "bare-path": "^3.0.0"
+ }
+ },
+ "node_modules/tar-stream": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.0.tgz",
+ "integrity": "sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "b4a": "^1.6.4",
+ "bare-fs": "^4.5.5",
+ "fast-fifo": "^1.2.0",
+ "streamx": "^2.15.0"
+ }
+ },
+ "node_modules/tdigest": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz",
+ "integrity": "sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==",
+ "license": "MIT",
+ "dependencies": {
+ "bintrees": "1.0.2"
+ }
+ },
+ "node_modules/teeny-request": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz",
+ "integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "http-proxy-agent": "^5.0.0",
+ "https-proxy-agent": "^5.0.0",
+ "node-fetch": "^2.6.9",
+ "stream-events": "^1.0.5",
+ "uuid": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/teeny-request/node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/teeny-request/node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/teeny-request/node_modules/uuid": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+ "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/teex": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz",
+ "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "streamx": "^2.12.5"
+ }
+ },
+ "node_modules/test-exclude": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.2.tgz",
+ "integrity": "sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@istanbuljs/schema": "^0.1.2",
+ "glob": "^10.4.1",
+ "minimatch": "^10.2.2"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/testcontainers": {
+ "version": "12.0.4",
+ "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-12.0.4.tgz",
+ "integrity": "sha512-QIR/8xF1+F/26cIM+9B4yyxNTbKJxAv3hygZyhPRgZ8Q2AhlPZjDdpXRuk16V37X4bgJRI3hXFhoEICMBA7Adg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@balena/dockerignore": "^1.0.2",
+ "@types/dockerode": "^4.0.1",
+ "archiver": "^7.0.1",
+ "async-lock": "^1.4.1",
+ "byline": "^5.0.0",
+ "debug": "^4.4.3",
+ "docker-compose": "^1.4.2",
+ "dockerode": "^5.0.0",
+ "get-port": "^5.1.1",
+ "proper-lockfile": "^4.1.2",
+ "properties-reader": "^3.0.1",
+ "ssh-remote-port-forward": "^1.0.4",
+ "tar-fs": "^3.1.2",
+ "tmp": "^0.2.7",
+ "undici": "^8.5.0"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "b4a": "^1.6.4"
+ }
+ },
+ "node_modules/text-extensions": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz",
+ "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/thread-stream": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.2.0.tgz",
+ "integrity": "sha512-zLBvqpwr4Esa0kRjcrzGU6zL25lePWaCLMx0RQFrmteozIfeNdaMLpG5U7PeHzvlFkAWaRKA9/KVW4F60iB+qw==",
+ "license": "MIT",
+ "dependencies": {
+ "real-require": "^0.2.0"
+ }
+ },
+ "node_modules/through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinybench": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
+ "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinyexec": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz",
+ "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==",
+ "dev": true,
+ "license": "MIT",
+ "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==",
+ "dev": true,
+ "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/tinypool": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz",
+ "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ }
+ },
+ "node_modules/tinyrainbow": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz",
+ "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/tinyspy": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz",
+ "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/tmp": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz",
+ "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.14"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/tree-kill": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+ "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "tree-kill": "cli.js"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
+ "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "node_modules/tsdown": {
+ "version": "0.22.14",
+ "resolved": "https://registry.npmjs.org/tsdown/-/tsdown-0.22.14.tgz",
+ "integrity": "sha512-ule7Y+fsAN2iZbLDoo7C4KYljFJNJJ+fLshyn+9gozeTspVersWHxwdGB+Dm2hzA38s6muFnUTl0jK3vJm9ifQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansis": "^4.3.1",
+ "cac": "^7.0.0",
+ "defu": "^6.1.7",
+ "empathic": "^2.0.1",
+ "hookable": "^6.1.1",
+ "import-without-cache": "^0.4.0",
+ "obug": "^2.1.4",
+ "picomatch": "^4.0.5",
+ "rolldown": "~1.2.0",
+ "rolldown-plugin-dts": "^0.27.13",
+ "tinyexec": "^1.2.4",
+ "tinyglobby": "^0.2.17",
+ "tree-kill": "^1.2.2",
+ "unconfig-core": "^7.5.0",
+ "verkit": "^0.3.0"
+ },
+ "bin": {
+ "tsdown": "dist/run.mjs"
+ },
+ "engines": {
+ "node": "^22.18.0 || >=24.11.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sxzz"
+ },
+ "peerDependencies": {
+ "@arethetypeswrong/core": "^0.18.1",
+ "@tsdown/css": "0.22.14",
+ "@tsdown/exe": "0.22.14",
+ "@vitejs/devtools": "*",
+ "publint": "^0.3.8",
+ "tsx": "*",
+ "typescript": "^5.0.0 || ^6.0.0 || ^7.0.0",
+ "unplugin-unused": "^0.5.0",
+ "unrun": "*"
+ },
+ "peerDependenciesMeta": {
+ "@arethetypeswrong/core": {
+ "optional": true
+ },
+ "@tsdown/css": {
+ "optional": true
+ },
+ "@tsdown/exe": {
+ "optional": true
+ },
+ "@vitejs/devtools": {
+ "optional": true
+ },
+ "publint": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "typescript": {
+ "optional": true
+ },
+ "unplugin-unused": {
+ "optional": true
+ },
+ "unrun": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tsdown/node_modules/cac": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/cac/-/cac-7.0.0.tgz",
+ "integrity": "sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.19.0"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/tsx": {
+ "version": "4.23.1",
+ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.1.tgz",
+ "integrity": "sha512-GQHnkIfxyx1wYCOS/wonik5MVRZU9hi1TEZmzGZSCJB1y9YgoZ8H6itNE/u4suE+yLmOzuE4E5S4TZ/ZX2wcWQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "~0.28.0"
+ },
+ "bin": {
+ "tsx": "dist/cli.mjs"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/aix-ppc64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz",
+ "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/android-arm": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz",
+ "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/android-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz",
+ "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/android-x64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz",
+ "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz",
+ "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/darwin-x64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz",
+ "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz",
+ "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz",
+ "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-arm": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz",
+ "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz",
+ "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-ia32": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz",
+ "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-loong64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz",
+ "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz",
+ "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz",
+ "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz",
+ "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-s390x": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz",
+ "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-x64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz",
+ "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz",
+ "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz",
+ "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz",
+ "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz",
+ "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz",
+ "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/sunos-x64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz",
+ "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/win32-arm64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz",
+ "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/win32-ia32": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz",
+ "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/win32-x64": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz",
+ "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/esbuild": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz",
+ "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.28.1",
+ "@esbuild/android-arm": "0.28.1",
+ "@esbuild/android-arm64": "0.28.1",
+ "@esbuild/android-x64": "0.28.1",
+ "@esbuild/darwin-arm64": "0.28.1",
+ "@esbuild/darwin-x64": "0.28.1",
+ "@esbuild/freebsd-arm64": "0.28.1",
+ "@esbuild/freebsd-x64": "0.28.1",
+ "@esbuild/linux-arm": "0.28.1",
+ "@esbuild/linux-arm64": "0.28.1",
+ "@esbuild/linux-ia32": "0.28.1",
+ "@esbuild/linux-loong64": "0.28.1",
+ "@esbuild/linux-mips64el": "0.28.1",
+ "@esbuild/linux-ppc64": "0.28.1",
+ "@esbuild/linux-riscv64": "0.28.1",
+ "@esbuild/linux-s390x": "0.28.1",
+ "@esbuild/linux-x64": "0.28.1",
+ "@esbuild/netbsd-arm64": "0.28.1",
+ "@esbuild/netbsd-x64": "0.28.1",
+ "@esbuild/openbsd-arm64": "0.28.1",
+ "@esbuild/openbsd-x64": "0.28.1",
+ "@esbuild/openharmony-arm64": "0.28.1",
+ "@esbuild/sunos-x64": "0.28.1",
+ "@esbuild/win32-arm64": "0.28.1",
+ "@esbuild/win32-ia32": "0.28.1",
+ "@esbuild/win32-x64": "0.28.1"
+ }
+ },
+ "node_modules/tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
+ "dev": true,
+ "license": "Unlicense"
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-is": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz",
+ "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==",
+ "license": "MIT",
+ "dependencies": {
+ "content-type": "^2.0.0",
+ "media-typer": "^1.1.0",
+ "mime-types": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/type-is/node_modules/content-type": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz",
+ "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/type-is/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",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/type-is/node_modules/mime-types": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
+ "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "^1.54.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/unconfig-core": {
+ "version": "7.5.0",
+ "resolved": "https://registry.npmjs.org/unconfig-core/-/unconfig-core-7.5.0.tgz",
+ "integrity": "sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@quansync/fs": "^1.0.0",
+ "quansync": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/undici": {
+ "version": "8.7.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-8.7.0.tgz",
+ "integrity": "sha512-N7iQtfyLhIMOFgQubvmLV26svHpO0bqKnAiWotTQCVKCmWrcGbBotPuW1x+xwYZ2VHdSTVUfPQQnlEt1/LouTQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=22.19.0"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "devOptional": true,
+ "license": "MIT"
+ },
+ "node_modules/uuid": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
+ "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
+ "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/verkit": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/verkit/-/verkit-0.3.1.tgz",
+ "integrity": "sha512-w2Eo8LSIIoW7qxNBzT7/17k+bh8plXo7G3dHjEIDqPlnluhzaxr9JX8F28VSYEtDvc1/a3WBDih6xNUZseebXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sxzz"
+ }
+ },
+ "node_modules/vite": {
+ "version": "5.4.21",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
+ "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.21.3",
+ "postcss": "^8.4.43",
+ "rollup": "^4.20.0"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || >=20.0.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite-node": {
+ "version": "2.1.9",
+ "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz",
+ "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cac": "^6.7.14",
+ "debug": "^4.3.7",
+ "es-module-lexer": "^1.5.4",
+ "pathe": "^1.1.2",
+ "vite": "^5.0.0"
+ },
+ "bin": {
+ "vite-node": "vite-node.mjs"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/vite-node/node_modules/pathe": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
+ "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vite/node_modules/@esbuild/aix-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/android-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+ "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/android-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+ "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/android-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+ "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+ "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/darwin-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+ "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+ "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+ "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+ "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+ "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+ "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-loong64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+ "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+ "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+ "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-s390x": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+ "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+ "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/sunos-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+ "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/win32-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+ "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/win32-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+ "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/win32-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+ "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/vite/node_modules/esbuild": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
+ "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.21.5",
+ "@esbuild/android-arm": "0.21.5",
+ "@esbuild/android-arm64": "0.21.5",
+ "@esbuild/android-x64": "0.21.5",
+ "@esbuild/darwin-arm64": "0.21.5",
+ "@esbuild/darwin-x64": "0.21.5",
+ "@esbuild/freebsd-arm64": "0.21.5",
+ "@esbuild/freebsd-x64": "0.21.5",
+ "@esbuild/linux-arm": "0.21.5",
+ "@esbuild/linux-arm64": "0.21.5",
+ "@esbuild/linux-ia32": "0.21.5",
+ "@esbuild/linux-loong64": "0.21.5",
+ "@esbuild/linux-mips64el": "0.21.5",
+ "@esbuild/linux-ppc64": "0.21.5",
+ "@esbuild/linux-riscv64": "0.21.5",
+ "@esbuild/linux-s390x": "0.21.5",
+ "@esbuild/linux-x64": "0.21.5",
+ "@esbuild/netbsd-x64": "0.21.5",
+ "@esbuild/openbsd-x64": "0.21.5",
+ "@esbuild/sunos-x64": "0.21.5",
+ "@esbuild/win32-arm64": "0.21.5",
+ "@esbuild/win32-ia32": "0.21.5",
+ "@esbuild/win32-x64": "0.21.5"
+ }
+ },
+ "node_modules/vitest": {
+ "version": "2.1.9",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz",
+ "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/expect": "2.1.9",
+ "@vitest/mocker": "2.1.9",
+ "@vitest/pretty-format": "^2.1.9",
+ "@vitest/runner": "2.1.9",
+ "@vitest/snapshot": "2.1.9",
+ "@vitest/spy": "2.1.9",
+ "@vitest/utils": "2.1.9",
+ "chai": "^5.1.2",
+ "debug": "^4.3.7",
+ "expect-type": "^1.1.0",
+ "magic-string": "^0.30.12",
+ "pathe": "^1.1.2",
+ "std-env": "^3.8.0",
+ "tinybench": "^2.9.0",
+ "tinyexec": "^0.3.1",
+ "tinypool": "^1.0.1",
+ "tinyrainbow": "^1.2.0",
+ "vite": "^5.0.0",
+ "vite-node": "2.1.9",
+ "why-is-node-running": "^2.3.0"
+ },
+ "bin": {
+ "vitest": "vitest.mjs"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "@edge-runtime/vm": "*",
+ "@types/node": "^18.0.0 || >=20.0.0",
+ "@vitest/browser": "2.1.9",
+ "@vitest/ui": "2.1.9",
+ "happy-dom": "*",
+ "jsdom": "*"
+ },
+ "peerDependenciesMeta": {
+ "@edge-runtime/vm": {
+ "optional": true
+ },
+ "@types/node": {
+ "optional": true
+ },
+ "@vitest/browser": {
+ "optional": true
+ },
+ "@vitest/ui": {
+ "optional": true
+ },
+ "happy-dom": {
+ "optional": true
+ },
+ "jsdom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vitest/node_modules/pathe": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
+ "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vitest/node_modules/tinyexec": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
+ "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+ "license": "BSD-2-Clause",
+ "optional": true
+ },
+ "node_modules/websocket-driver": {
+ "version": "0.7.5",
+ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.5.tgz",
+ "integrity": "sha512-ZL2+3c7kMBdIRCMz6l8jQMHyGVxj+UL+xVk74Ombiciboca8rHa15L86B19E5oh1pL9Ii/uj54gtsIrZGMo6zA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "http-parser-js": ">=0.5.1",
+ "safe-buffer": ">=5.1.0",
+ "websocket-extensions": ">=0.1.1"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/websocket-extensions": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
+ "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
+ "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/why-is-node-running": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
+ "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "siginfo": "^2.0.0",
+ "stackback": "0.0.2"
+ },
+ "bin": {
+ "why-is-node-running": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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==",
+ "dev": true,
+ "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/wrap-ansi-cjs": {
+ "name": "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-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "license": "ISC"
+ },
+ "node_modules/xml-naming": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.3.0.tgz",
+ "integrity": "sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4"
+ }
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "devOptional": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "license": "ISC"
+ },
+ "node_modules/yaml": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
+ "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/eemeli"
+ }
+ },
+ "node_modules/yargs": {
+ "version": "17.7.3",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz",
+ "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "devOptional": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yargs/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==",
+ "devOptional": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yargs/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==",
+ "devOptional": true,
+ "license": "MIT"
+ },
+ "node_modules/yargs/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==",
+ "devOptional": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yargs/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==",
+ "devOptional": true,
+ "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/yargs/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==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "devOptional": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/yuku-ast": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/yuku-ast/-/yuku-ast-0.8.1.tgz",
+ "integrity": "sha512-+k1E2f08y0k1+vpdD1KUCzDh2JXvwirMa8YR2Jr7VCS4zAo3eT5A/HbJCqaVGd6idaPY0gwLM9C4seEY4h10Hw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@yuku-toolchain/types": "^0.8.1"
+ }
+ },
+ "node_modules/yuku-codegen": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/yuku-codegen/-/yuku-codegen-0.8.1.tgz",
+ "integrity": "sha512-/4Sbg9K9HpCe3PidmMbs9TzJ62gq3KmQY279TB0EGKdMoM8LfcmIg4E9wS2J/ylbFuQaWcYqf5dBKr8zubdlKA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@yuku-toolchain/types": "^0.8.1"
+ },
+ "optionalDependencies": {
+ "@yuku-codegen/binding-darwin-arm64": "0.8.1",
+ "@yuku-codegen/binding-darwin-x64": "0.8.1",
+ "@yuku-codegen/binding-freebsd-x64": "0.8.1",
+ "@yuku-codegen/binding-linux-arm-gnu": "0.8.1",
+ "@yuku-codegen/binding-linux-arm-musl": "0.8.1",
+ "@yuku-codegen/binding-linux-arm64-gnu": "0.8.1",
+ "@yuku-codegen/binding-linux-arm64-musl": "0.8.1",
+ "@yuku-codegen/binding-linux-x64-gnu": "0.8.1",
+ "@yuku-codegen/binding-linux-x64-musl": "0.8.1",
+ "@yuku-codegen/binding-win32-arm64": "0.8.1",
+ "@yuku-codegen/binding-win32-x64": "0.8.1"
+ }
+ },
+ "node_modules/yuku-parser": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/yuku-parser/-/yuku-parser-0.8.1.tgz",
+ "integrity": "sha512-YvUz2jdFMIq0QjN8LmI32ox+RBCn3l8VToAXVQ5QFfLTxSxmGZS6JP80ptePEju+CpeTdINLmUm7Ewodzh1QnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@yuku-toolchain/types": "^0.8.1",
+ "yuku-ast": "^0.8.1"
+ },
+ "optionalDependencies": {
+ "@yuku-parser/binding-darwin-arm64": "0.8.1",
+ "@yuku-parser/binding-darwin-x64": "0.8.1",
+ "@yuku-parser/binding-freebsd-x64": "0.8.1",
+ "@yuku-parser/binding-linux-arm-gnu": "0.8.1",
+ "@yuku-parser/binding-linux-arm-musl": "0.8.1",
+ "@yuku-parser/binding-linux-arm64-gnu": "0.8.1",
+ "@yuku-parser/binding-linux-arm64-musl": "0.8.1",
+ "@yuku-parser/binding-linux-x64-gnu": "0.8.1",
+ "@yuku-parser/binding-linux-x64-musl": "0.8.1",
+ "@yuku-parser/binding-win32-arm64": "0.8.1",
+ "@yuku-parser/binding-win32-x64": "0.8.1"
+ }
+ },
+ "node_modules/zip-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz",
+ "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "archiver-utils": "^5.0.0",
+ "compress-commons": "^6.0.2",
+ "readable-stream": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/zip-stream/node_modules/readable-stream": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
+ "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "abort-controller": "^3.0.0",
+ "buffer": "^6.0.3",
+ "events": "^3.3.0",
+ "process": "^0.11.10",
+ "string_decoder": "^1.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/zod": {
+ "version": "3.25.76",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
+ "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zod-to-json-schema": {
+ "version": "3.25.2",
+ "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz",
+ "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==",
+ "license": "ISC",
+ "peerDependencies": {
+ "zod": "^3.25.28 || ^4"
+ }
+ },
+ "packages/mcp": {
+ "name": "@notifkit/mcp",
+ "version": "0.0.1",
+ "dependencies": {
+ "@cfworker/json-schema": "^4.1.1",
+ "@modelcontextprotocol/sdk": "^1.30.0",
+ "zod": "^3.25.76"
+ },
+ "bin": {
+ "notifkit-mcp": "dist/index.js"
+ },
+ "devDependencies": {
+ "@types/node": "^22.0.0",
+ "typescript": "^5.8.3"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ }
+ },
+ "packages/provider-console": {
+ "name": "@notifkit/provider-console",
+ "version": "0.0.1",
+ "devDependencies": {
+ "notifkit": "file:../../",
+ "typescript": "^5.8.3"
+ },
+ "peerDependencies": {
+ "notifkit": "*"
+ }
+ },
+ "packages/provider-fcm": {
+ "name": "@notifkit/provider-fcm",
+ "version": "0.0.1",
+ "dependencies": {
+ "firebase-admin": "^12.0.0"
+ },
+ "devDependencies": {
+ "notifkit": "file:../../",
+ "typescript": "^5.8.3"
+ },
+ "peerDependencies": {
+ "notifkit": "*"
+ }
+ },
+ "packages/provider-resend": {
+ "name": "@notifkit/provider-resend",
+ "version": "0.0.1",
+ "dependencies": {
+ "react": "^19.2.7",
+ "react-dom": "^19.2.7",
+ "resend": "^3.0.0",
+ "svix": "^1.99.1"
+ },
+ "devDependencies": {
+ "notifkit": "file:../../",
+ "typescript": "^5.8.3"
+ },
+ "peerDependencies": {
+ "notifkit": "*"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..733ba25
--- /dev/null
+++ b/package.json
@@ -0,0 +1,110 @@
+{
+ "name": "notifkit",
+ "version": "0.1.0",
+ "description": "Self-hosted notification infrastructure. One call delivers to email, SMS, push, and webhook โ routed by preference, quiet hours, and consent.",
+ "license": "MIT",
+ "author": "devkitshq",
+ "homepage": "https://notifkit.dev",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/devkitshq/notifkit.git"
+ },
+ "bugs": {
+ "url": "https://github.com/devkitshq/notifkit/issues"
+ },
+ "keywords": [
+ "notifications",
+ "notification-system",
+ "email",
+ "sms",
+ "push-notifications",
+ "webhooks",
+ "self-hosted",
+ "queue",
+ "redis-streams",
+ "postgres",
+ "drizzle",
+ "transactional-email",
+ "user-preferences",
+ "quiet-hours",
+ "unsubscribe",
+ "multi-tenant",
+ "typescript"
+ ],
+ "workspaces": [
+ "packages/*"
+ ],
+ "type": "module",
+ "main": "dist/index.mjs",
+ "types": "dist/index.d.mts",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.mts",
+ "default": "./dist/index.mjs"
+ },
+ "./package.json": "./package.json"
+ },
+ "files": [
+ "dist",
+ "drizzle",
+ "README.md",
+ "LICENSE"
+ ],
+ "scripts": {
+ "build": "tsdown",
+ "dev": "tsdown --watch",
+ "test": "vitest run --coverage",
+ "test:watch": "vitest",
+ "lint": "eslint . --fix",
+ "typecheck": "tsc --noEmit",
+ "prepare": "husky",
+ "prepublishOnly": "npm run typecheck && npm run build"
+ },
+ "dependencies": {
+ "ai": "^7.0.37",
+ "dotenv": "^16.4.5",
+ "drizzle-orm": "^0.45.2",
+ "drizzle-zod": "^0.8.3",
+ "ioredis": "^5.4.1",
+ "pino": "^9.2.0",
+ "pino-pretty": "^11.0.0",
+ "postgres": "^3.4.4",
+ "prom-client": "^15.1.3",
+ "zod": "^3.23.8"
+ },
+ "devDependencies": {
+ "@commitlint/cli": "^19.3.0",
+ "@commitlint/config-conventional": "^19.2.2",
+ "@testcontainers/postgresql": "^12.0.4",
+ "@testcontainers/redis": "^12.0.4",
+ "@types/node": "^22.0.0",
+ "@types/supertest": "^7.2.1",
+ "@typescript-eslint/eslint-plugin": "^8.0.0",
+ "@typescript-eslint/parser": "^8.0.0",
+ "@vitest/coverage-v8": "^2.0.0",
+ "drizzle-kit": "^0.31.10",
+ "eslint": "^9.0.0",
+ "husky": "^9.0.11",
+ "lint-staged": "^15.2.5",
+ "prettier": "^3.3.0",
+ "supertest": "^7.2.2",
+ "testcontainers": "^12.0.4",
+ "tsdown": "^0.22.14",
+ "typescript": "^5.8.3",
+ "vitest": "^2.0.0"
+ },
+ "lint-staged": {
+ "*.{ts,tsx}": [
+ "eslint --fix",
+ "prettier --write"
+ ],
+ "*.{json,md,yaml,yml}": [
+ "prettier --write"
+ ]
+ },
+ "engines": {
+ "node": ">=22.0.0",
+ "npm": ">=10.0.0"
+ },
+ "packageManager": "npm@10.8.1"
+}
diff --git a/packages/mcp/LICENSE b/packages/mcp/LICENSE
new file mode 100644
index 0000000..235464d
--- /dev/null
+++ b/packages/mcp/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 devkitshq
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/mcp/README.md b/packages/mcp/README.md
new file mode 100644
index 0000000..2daa1dd
--- /dev/null
+++ b/packages/mcp/README.md
@@ -0,0 +1,127 @@
+# @notifkit/mcp
+
+An [MCP](https://modelcontextprotocol.io) server for notifkit. Point a terminal agent at your notifkit deployment and it can send notifications, manage templates and users, build workflows, and read the delivery log โ without you writing a script for each one.
+
+Speaks stdio, so it works with Claude Code, Claude Desktop, and any other MCP client.
+
+## Install
+
+```sh
+npm install -g @notifkit/mcp
+```
+
+Or skip the install and let your client run it with `npx -y @notifkit/mcp`.
+
+## Configure
+
+Three environment variables:
+
+| Variable | Required | Notes |
+| --------------------- | ---------------------- | ------------------------------------------ |
+| `NOTIFKIT_API_KEY` | yes | A project API key, or your `ADMIN_API_KEY` |
+| `NOTIFKIT_URL` | no | Defaults to `http://localhost:3000` |
+| `NOTIFKIT_PROJECT_ID` | only with an admin key | Which project the admin key should act on |
+
+A project-scoped key already carries its project, so it needs no `NOTIFKIT_PROJECT_ID`. Mint one with `POST /v1/projects/:id/keys`. Prefer that over the admin key โ it is scoped to a single project and can be revoked on its own.
+
+### Claude Code
+
+```sh
+claude mcp add notifkit \
+ --env NOTIFKIT_URL=http://localhost:3000 \
+ --env NOTIFKIT_API_KEY=nk_your_project_key \
+ -- npx -y @notifkit/mcp
+```
+
+### Claude Desktop
+
+In `claude_desktop_config.json`:
+
+```json
+{
+ "mcpServers": {
+ "notifkit": {
+ "command": "npx",
+ "args": ["-y", "@notifkit/mcp"],
+ "env": {
+ "NOTIFKIT_URL": "http://localhost:3000",
+ "NOTIFKIT_API_KEY": "nk_your_project_key"
+ }
+ }
+ }
+}
+```
+
+## The two-sentence workflow
+
+The thing this exists for:
+
+> **You:** "Send the spring-sale template to these 200 addresses." _(paste)_
+> **Agent:** โ `send_campaign` โ "Queued 200 as `spring-sale-aug-13`."
+>
+> _(next day)_ **You:** "How did the spring sale do?"
+> **Agent:** โ `list_campaigns` โ `get_campaign_stats` โ "194 delivered, 4 bounced, 2 suppressed. 71 opened (37%), 12 clicked (6%)."
+
+`send_campaign` takes raw email addresses โ no user records needed up front. It creates one per address, keyed off the address itself, so re-sending to the same list updates those people rather than duplicating them. Duplicates and case differences within a list are collapsed before sending.
+
+## Tools
+
+**Campaigns**
+
+| Tool | What it does |
+| -------------------- | -------------------------------------------------------------------------------------------- |
+| `send_campaign` | Send a template to a list of email addresses, tagged with a label you can report on later |
+| `list_campaigns` | Recent campaign labels with size and last activity โ how "the one from yesterday" gets found |
+| `get_campaign_stats` | Sent, delivered, failed, opened, clicked, bounced, complained, unsubscribed, with rates |
+
+**Sending**
+
+| Tool | What it does |
+| ------------------- | --------------------------------------------------------------------------------------------------------------- |
+| `send_notification` | Send to existing users, a segment, or a topic. Takes `sendAt` to schedule and `campaign` to make it reportable. |
+| `list_segments` | Every segment tag in use โ the valid audiences to broadcast to |
+| `list_scheduled` | Sends queued for the future, including quiet-hours deferrals |
+| `get_delivery_logs` | Per-message detail, when the campaign totals are not enough |
+
+**Suppressions** โ `list_suppressions`, `suppress_address`, `unsuppress_address`
+
+**Templates** โ `list_templates`, `get_template`, `upsert_template`
+
+**Users** โ `list_users`, `get_user`, `upsert_user`, `add_user_contact`
+
+**Workflows** โ `create_workflow`, `list_workflows`, `trigger_workflow`, `get_workflow_run`, `cancel_workflow_run`, `ingest_event`
+
+**Operations** โ `get_system_health`, `get_dead_letters`, `replay_dead_letter`
+
+Project and API-key management is deliberately not exposed: minting and revoking credentials is not something an agent should do on your behalf. Use the HTTP API for that.
+
+## Reading the numbers
+
+**Audience size is not delivery count.** Every send passes the same gates as any other notification โ suppressions, opt-outs, quiet hours, throttling โ so a list of 200 delivers to fewer than 200, by design. `send_campaign` reports what was _queued_; `get_campaign_stats` reports what actually happened.
+
+**An untracked open rate is not a zero open rate.** Opens and clicks arrive from provider webhooks. On SMS and push there is nothing to report; on email, nothing arrives unless the webhook is configured. `get_campaign_stats` returns a `warnings` array saying which of those applies, and the tool description tells the agent to pass it on rather than reporting a flat 0%.
+
+**Watch the complaint rate.** Above roughly 0.3%, mailbox providers start throttling or junking your mail โ and that hits your transactional email too, since it is the same sending domain.
+
+## Sequences
+
+Multi-step sends โ onboarding, drips, reminders โ are workflows rather than campaigns: `create_workflow` defines the steps, `trigger_workflow` enrols each user.
+
+> _"Make an onboarding drip: welcome email, wait 3 days, then the tips email unless they've already sent an `activated` event."_
+
+## Safety
+
+`send_campaign`, `send_notification`, `trigger_workflow`, and `replay_dead_letter` reach real people and cannot be recalled. Read-only tools are annotated as such so clients can auto-approve them and prompt on the rest; `cancel_workflow_run` and `unsuppress_address` are marked destructive.
+
+Suppressions deserve particular care. A suppressed address is usually someone who asked not to be contacted, or an address that no longer exists. Removing a suppression to improve a delivery figure is how a sending domain gets blocked โ the tool description tells the agent to confirm first, and you should treat an unprompted suggestion to do it as a red flag.
+
+Point the server at a staging deployment while you get a feel for it.
+
+## Development
+
+```sh
+npm run build # tsc
+npm run typecheck
+```
+
+The server holds no state and imports nothing from the notifkit core package โ it is a thin, self-contained HTTP client, so running it costs you a Node process and nothing else.
diff --git a/packages/mcp/package.json b/packages/mcp/package.json
new file mode 100644
index 0000000..320c1de
--- /dev/null
+++ b/packages/mcp/package.json
@@ -0,0 +1,47 @@
+{
+ "name": "@notifkit/mcp",
+ "version": "0.1.0",
+ "description": "Model Context Protocol server for notifkit โ drive sends, templates, users, and workflows from a terminal agent.",
+ "type": "module",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "bin": {
+ "notifkit-mcp": "dist/index.js"
+ },
+ "files": [
+ "dist",
+ "README.md",
+ "LICENSE"
+ ],
+ "scripts": {
+ "build": "tsc",
+ "dev": "tsc --watch",
+ "typecheck": "tsc --noEmit"
+ },
+ "dependencies": {
+ "@cfworker/json-schema": "^4.1.1",
+ "@modelcontextprotocol/sdk": "^1.30.0",
+ "zod": "^3.25.76"
+ },
+ "devDependencies": {
+ "@types/node": "^22.0.0",
+ "typescript": "^5.8.3"
+ },
+ "engines": {
+ "node": ">=22.0.0"
+ },
+ "license": "MIT",
+ "author": "devkitshq",
+ "homepage": "https://notifkit.dev",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/devkitshq/notifkit.git",
+ "directory": "packages/mcp"
+ },
+ "bugs": {
+ "url": "https://github.com/devkitshq/notifkit/issues"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/packages/mcp/src/client.ts b/packages/mcp/src/client.ts
new file mode 100644
index 0000000..d94cb86
--- /dev/null
+++ b/packages/mcp/src/client.ts
@@ -0,0 +1,101 @@
+/**
+ * A minimal HTTP client for the notifkit API.
+ *
+ * Deliberately self-contained rather than importing `NotifkitClient` from the
+ * root package: that entrypoint re-exports the db, redis, and config modules,
+ * which would pull Postgres and Redis drivers into a process whose only job is
+ * to speak JSON-RPC over stdio.
+ */
+
+export interface NotifkitApiOptions {
+ baseUrl: string;
+ apiKey: string;
+ /** Required when `apiKey` is the admin key; project-scoped keys carry their own project. */
+ projectId?: string;
+}
+
+export class NotifkitApiError extends Error {
+ constructor(
+ message: string,
+ readonly status: number,
+ readonly body: unknown,
+ ) {
+ super(message);
+ this.name = "NotifkitApiError";
+ }
+}
+
+export class NotifkitApi {
+ private readonly baseUrl: string;
+ private readonly headers: Record;
+
+ constructor(options: NotifkitApiOptions) {
+ this.baseUrl = options.baseUrl.replace(/\/$/, "");
+ this.headers = {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${options.apiKey}`,
+ ...(options.projectId ? { "x-project-id": options.projectId } : {}),
+ };
+ }
+
+ async request(
+ method: string,
+ path: string,
+ options?: { body?: unknown; query?: Record },
+ ): Promise {
+ let url = `${this.baseUrl}${path}`;
+
+ if (options?.query) {
+ const params = new URLSearchParams();
+ for (const [key, value] of Object.entries(options.query)) {
+ if (value !== undefined) params.set(key, String(value));
+ }
+ const qs = params.toString();
+ if (qs) url += `?${qs}`;
+ }
+
+ const res = await fetch(url, {
+ method,
+ headers: this.headers,
+ body: options?.body === undefined ? undefined : JSON.stringify(options.body),
+ });
+
+ if (res.status === 204) return undefined as T;
+
+ const text = await res.text();
+ let data: unknown = undefined;
+ if (text) {
+ try {
+ data = JSON.parse(text);
+ } catch {
+ data = text;
+ }
+ }
+
+ if (!res.ok) {
+ const record =
+ typeof data === "object" && data !== null ? (data as Record) : {};
+ const message =
+ typeof record["message"] === "string"
+ ? record["message"]
+ : typeof record["error"] === "string"
+ ? record["error"]
+ : `Request failed with status ${res.status}`;
+ throw new NotifkitApiError(message, res.status, data);
+ }
+
+ return data as T;
+ }
+
+ get(path: string, query?: Record): Promise {
+ return this.request("GET", path, { query });
+ }
+
+ post(path: string, body?: unknown): Promise {
+ return this.request("POST", path, { body });
+ }
+
+ put(path: string, body?: unknown): Promise {
+ return this.request("PUT", path, { body });
+ }
+}
diff --git a/packages/mcp/src/index.ts b/packages/mcp/src/index.ts
new file mode 100644
index 0000000..4af45f9
--- /dev/null
+++ b/packages/mcp/src/index.ts
@@ -0,0 +1,71 @@
+#!/usr/bin/env node
+/**
+ * notifkit MCP server (stdio).
+ *
+ * Exposes the notifkit HTTP API as MCP tools so a terminal agent can send
+ * notifications, manage templates and users, and drive workflows.
+ *
+ * stdout is the JSON-RPC transport โ never write to it. Diagnostics go to stderr.
+ */
+
+import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
+import { NotifkitApi } from "./client.js";
+import { registerTools } from "./tools.js";
+
+const DEFAULT_URL = "http://localhost:3000";
+
+export function createServer(options: {
+ baseUrl: string;
+ apiKey: string;
+ projectId?: string;
+}): McpServer {
+ const server = new McpServer(
+ { name: "notifkit", version: "0.0.1" },
+ {
+ instructions:
+ "Tools for notifkit, a self-hosted notification service.\n\n" +
+ "Sending: send_campaign takes a list of email addresses directly and is the right tool when the " +
+ "user supplies recipients themselves. send_notification targets existing users, a segment, or a " +
+ "topic. Both accept a `campaign` label โ set one on any send whose results might be asked about " +
+ "later, and tell the user the label, because without it the send cannot be reported on " +
+ "afterwards. Multi-step sequences (drips, onboarding, reminders) are workflows: create_workflow " +
+ "defines one, trigger_workflow runs it per user.\n\n" +
+ "Reporting: get_campaign_stats answers 'how did that send do?'. Pass on its `warnings` verbatim โ " +
+ "an untracked open rate is not a zero open rate.\n\n" +
+ "Discover ids with list_campaigns, list_segments, list_templates, and list_workflows rather than " +
+ "guessing them.\n\n" +
+ "Every send reaches real people and cannot be recalled, so confirm the audience before " +
+ "broadcasting. Never remove a suppression to improve a delivery figure: suppressions record that " +
+ "somebody asked not to be contacted.",
+ },
+ );
+
+ registerTools(server, new NotifkitApi(options));
+ return server;
+}
+
+async function main(): Promise {
+ const baseUrl = process.env["NOTIFKIT_URL"] ?? DEFAULT_URL;
+ const apiKey = process.env["NOTIFKIT_API_KEY"];
+ const projectId = process.env["NOTIFKIT_PROJECT_ID"];
+
+ if (!apiKey) {
+ console.error(
+ "notifkit-mcp: NOTIFKIT_API_KEY is not set.\n" +
+ " Set it to a project API key, or to your ADMIN_API_KEY together with NOTIFKIT_PROJECT_ID.\n" +
+ ` NOTIFKIT_URL defaults to ${DEFAULT_URL}.`,
+ );
+ process.exit(1);
+ }
+
+ const server = createServer({ baseUrl, apiKey, projectId });
+ await server.connect(new StdioServerTransport());
+
+ console.error(`notifkit-mcp: connected, talking to ${baseUrl}`);
+}
+
+main().catch((error: unknown) => {
+ console.error("notifkit-mcp: fatal:", error instanceof Error ? error.message : error);
+ process.exit(1);
+});
diff --git a/packages/mcp/src/tools.ts b/packages/mcp/src/tools.ts
new file mode 100644
index 0000000..e155aee
--- /dev/null
+++ b/packages/mcp/src/tools.ts
@@ -0,0 +1,747 @@
+import { z } from "zod";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import type { NotifkitApi } from "./client.js";
+import { NotifkitApiError } from "./client.js";
+
+const CHANNELS = ["email", "sms", "push", "webhook", "in-app"] as const;
+const CONTACT_CHANNELS = ["email", "sms", "push", "webhook"] as const;
+const PRIORITIES = ["low", "normal", "high", "critical"] as const;
+
+interface ToolResult {
+ // The SDK's tool-result type carries an index signature for protocol
+ // extensions; without one here an interface is not assignable to it.
+ [key: string]: unknown;
+ content: { type: "text"; text: string }[];
+ isError?: boolean;
+}
+
+/** Render any handler return value as the text payload MCP clients expect. */
+function ok(value: unknown): ToolResult {
+ const text =
+ value === undefined
+ ? "Done."
+ : typeof value === "string"
+ ? value
+ : JSON.stringify(value, null, 2);
+ return { content: [{ type: "text", text }] };
+}
+
+function fail(error: unknown): ToolResult {
+ const text =
+ error instanceof NotifkitApiError
+ ? `notifkit API error (HTTP ${error.status}): ${error.message}`
+ : error instanceof Error
+ ? error.message
+ : String(error);
+ return { content: [{ type: "text", text }], isError: true };
+}
+
+/** Wrap a handler so a failed request is reported to the model instead of killing the server. */
+function handler(fn: (args: T) => Promise) {
+ return async (args: T): Promise => {
+ try {
+ return ok(await fn(args));
+ } catch (error) {
+ return fail(error);
+ }
+ };
+}
+
+export function registerTools(server: McpServer, api: NotifkitApi): void {
+ // โโโ Sending โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+ server.registerTool(
+ "send_notification",
+ {
+ title: "Send a notification",
+ description:
+ "Queue a notification and return immediately with its message id. This is the primitive behind " +
+ "both one-off sends and broadcasts: target a single user by id, a list of user ids, a `segment` " +
+ "(every user tagged with it), or a `topic` (every user opted in to it). Exactly one of `user`, " +
+ "`segment`, or `topic` must be given. Set `sendAt` to schedule a future send, which can then be " +
+ "inspected with list_scheduled. Delivery is filtered by each recipient's preferences, consent, and " +
+ "quiet hours, so the number of messages actually delivered is usually lower than the audience size. " +
+ "Call list_segments first when the user names an audience you have not confirmed exists, and " +
+ "list_templates when you are unsure of the template id.",
+ inputSchema: {
+ template: z.string().min(1).describe("Template id to render, e.g. 'order-shipped'."),
+ user: z
+ .union([z.string().min(1), z.array(z.string().min(1)).nonempty()])
+ .optional()
+ .describe(
+ "A single user id or a list of user ids. Mutually exclusive with segment and topic.",
+ ),
+ segment: z
+ .string()
+ .min(1)
+ .optional()
+ .describe(
+ "Send to every user tagged with this segment. Mutually exclusive with user and topic.",
+ ),
+ topic: z
+ .string()
+ .min(1)
+ .optional()
+ .describe(
+ "Send to every user opted in to this topic. Mutually exclusive with user and segment.",
+ ),
+ data: z
+ .record(z.unknown())
+ .optional()
+ .describe("Variables interpolated into the template's {{placeholders}}."),
+ channels: z
+ .array(z.enum(CHANNELS))
+ .nonempty()
+ .optional()
+ .describe("Channels to use. Defaults to whatever the template defines."),
+ fallback: z
+ .boolean()
+ .optional()
+ .describe(
+ "When true, channels are tried in order and only the first success counts. When false or " +
+ "omitted, every listed channel is delivered to.",
+ ),
+ priority: z
+ .enum(PRIORITIES)
+ .optional()
+ .describe("Queue lane. 'critical' bypasses quiet hours and per-user rate limits."),
+ sendAt: z
+ .string()
+ .datetime()
+ .optional()
+ .describe("ISO 8601 timestamp to send at. Omit to send now."),
+ campaign: z
+ .string()
+ .min(1)
+ .max(128)
+ .optional()
+ .describe(
+ "Label grouping this send for later reporting via get_campaign_stats. Set it on any send " +
+ "whose results someone may ask about; without it the messages cannot be attributed.",
+ ),
+ },
+ annotations: { title: "Send a notification", readOnlyHint: false, idempotentHint: false },
+ },
+ handler(async (args) => {
+ const targets = [args.user, args.segment, args.topic].filter((t) => t !== undefined);
+ if (targets.length !== 1) {
+ throw new Error("Provide exactly one of `user`, `segment`, or `topic`.");
+ }
+ return api.post("/v1/notify", args);
+ }),
+ );
+
+ server.registerTool(
+ "send_campaign",
+ {
+ title: "Send a campaign to a list of addresses",
+ description:
+ "Send one template to a list of email addresses given directly โ the tool creates or updates a " +
+ "user record for each address, then sends, and tags every message with a campaign label so the " +
+ "results can be read back later with get_campaign_stats. Use this when the person supplies the " +
+ "recipients themselves (a pasted list, a spreadsheet column). Use send_notification instead when " +
+ "targeting existing users, a segment, or a topic. Always report the returned campaign label back " +
+ "to the user โ it is how they ask about results later. Suppressed addresses (previous " +
+ "unsubscribes, complaints, hard bounces) are dropped automatically at send time, so the delivered " +
+ "count is normally lower than the list size.",
+ inputSchema: {
+ campaign: z
+ .string()
+ .min(1)
+ .max(128)
+ .describe(
+ "Label for this send, e.g. 'spring-sale-2026-08-13'. Must be unique per send โ reusing a " +
+ "label merges the two into one set of statistics.",
+ ),
+ template: z.string().min(1).describe("Template id to render."),
+ emails: z
+ .array(z.string().email())
+ .min(1)
+ .max(10000)
+ .describe("Recipient email addresses."),
+ data: z
+ .record(z.unknown())
+ .optional()
+ .describe("Variables interpolated into the template, shared by every recipient."),
+ priority: z
+ .enum(PRIORITIES)
+ .optional()
+ .describe(
+ "Queue lane. Leave unset for 'normal'; do not use 'critical' for marketing sends.",
+ ),
+ sendAt: z
+ .string()
+ .datetime()
+ .optional()
+ .describe("ISO 8601 timestamp to send at. Omit to send now."),
+ },
+ annotations: { title: "Send a campaign", readOnlyHint: false, idempotentHint: false },
+ },
+ handler(async (args) => {
+ // The API needs a stable user id per address. Deriving it from the
+ // address itself means re-sending to the same list updates those people
+ // rather than creating a second copy of each.
+ const seen = new Set();
+ const recipients = [];
+ for (const raw of args.emails) {
+ const email = raw.trim().toLowerCase();
+ if (seen.has(email)) continue;
+ seen.add(email);
+ recipients.push({ id: `email:${email}`, email });
+ }
+
+ const result = await api.post>("/v1/notify", {
+ user: recipients,
+ template: args.template,
+ campaign: args.campaign,
+ channels: ["email"],
+ ...(args.data ? { data: args.data } : {}),
+ ...(args.priority ? { priority: args.priority } : {}),
+ ...(args.sendAt ? { sendAt: args.sendAt } : {}),
+ });
+
+ const duplicates = args.emails.length - recipients.length;
+ return {
+ campaign: args.campaign,
+ queued: recipients.length,
+ ...(duplicates > 0 ? { duplicatesRemoved: duplicates } : {}),
+ note:
+ "Queued, not yet delivered. Preferences, quiet hours, and suppressions are applied during " +
+ "delivery โ call get_campaign_stats for what actually landed.",
+ result,
+ };
+ }),
+ );
+
+ server.registerTool(
+ "list_campaigns",
+ {
+ title: "List campaigns",
+ description:
+ "List recent campaign labels with their size and when they last saw activity, newest first. Call " +
+ "this when the user refers to a past send without naming its exact label ('the one I sent " +
+ "yesterday') to find the label to pass to get_campaign_stats.",
+ inputSchema: {
+ limit: z
+ .number()
+ .int()
+ .min(1)
+ .max(100)
+ .optional()
+ .describe("How many campaigns to return."),
+ },
+ annotations: { title: "List campaigns", readOnlyHint: true },
+ },
+ handler(async (args) => api.get("/v1/campaigns", args)),
+ );
+
+ server.registerTool(
+ "get_campaign_stats",
+ {
+ title: "Get campaign performance",
+ description:
+ "The delivery and engagement funnel for one campaign: sent, delivered, failed, opened, clicked, " +
+ "bounced, complained, unsubscribed, with rates and a per-channel breakdown. This is the tool for " +
+ "'how did that send perform?'. Every count is people, not events โ opening an email twice counts " +
+ "once. Always surface the `warnings` field to the user: it says when opens and clicks are not " +
+ "tracked on a channel, which is different from them being zero. A high complaint rate (above " +
+ "~0.3%) is worth flagging unprompted, as it damages deliverability for all future sends.",
+ inputSchema: {
+ campaign: z.string().min(1).describe("Campaign label, as returned by list_campaigns."),
+ },
+ annotations: { title: "Get campaign performance", readOnlyHint: true },
+ },
+ handler(async (args) => api.get(`/v1/campaigns/${encodeURIComponent(args.campaign)}/stats`)),
+ );
+
+ server.registerTool(
+ "list_suppressions",
+ {
+ title: "List suppressed addresses",
+ description:
+ "List destinations that will not be contacted again, with the reason each was suppressed " +
+ "(unsubscribed, complained, bounced, or manual). Call this to explain why someone did not receive " +
+ "a message, or to review unsubscribes after a campaign.",
+ inputSchema: {
+ limit: z.number().int().min(1).max(500).optional().describe("How many rows to return."),
+ channel: z.enum(CONTACT_CHANNELS).optional().describe("Only this channel."),
+ reason: z
+ .enum(["unsubscribed", "complained", "bounced", "manual"])
+ .optional()
+ .describe("Only suppressions with this reason."),
+ },
+ annotations: { title: "List suppressed addresses", readOnlyHint: true },
+ },
+ handler(async (args) => api.get("/v1/suppressions", args)),
+ );
+
+ server.registerTool(
+ "suppress_address",
+ {
+ title: "Suppress an address",
+ description:
+ "Stop sending to a destination on a channel โ use when someone asks to be removed by replying, " +
+ "over the phone, or any route the provider does not report automatically. Takes effect on the " +
+ "next send and outranks every other setting, including 'critical' priority.",
+ inputSchema: {
+ channel: z.enum(CONTACT_CHANNELS).describe("Channel to suppress on."),
+ target: z.string().min(1).describe("The address: email, phone number, or push token."),
+ reason: z
+ .enum(["unsubscribed", "complained", "bounced", "manual"])
+ .optional()
+ .describe("Why. Defaults to 'manual'."),
+ },
+ annotations: { title: "Suppress an address", readOnlyHint: false, idempotentHint: true },
+ },
+ handler(async (args) => api.post("/v1/suppressions", args)),
+ );
+
+ server.registerTool(
+ "unsuppress_address",
+ {
+ title: "Remove a suppression",
+ description:
+ "Re-enable sending to a previously suppressed destination. Only do this on an explicit request " +
+ "from the user โ a suppression usually records that the person asked not to be contacted, and " +
+ "removing it to fix a low delivery count is how a sending domain gets blocked. Confirm before " +
+ "calling.",
+ inputSchema: {
+ channel: z.enum(CONTACT_CHANNELS).describe("Channel the suppression is on."),
+ target: z.string().min(1).describe("The suppressed address."),
+ },
+ annotations: {
+ title: "Remove a suppression",
+ readOnlyHint: false,
+ destructiveHint: true,
+ idempotentHint: true,
+ },
+ },
+ handler(async (args) => {
+ await api.request(
+ "DELETE",
+ `/v1/suppressions/${encodeURIComponent(args.channel)}/${encodeURIComponent(args.target)}`,
+ );
+ return `Removed the suppression on ${args.target} for ${args.channel}.`;
+ }),
+ );
+
+ server.registerTool(
+ "list_segments",
+ {
+ title: "List segments",
+ description:
+ "List every segment tag currently in use across the project's users. Call this to discover the " +
+ "valid audiences for send_notification's `segment` argument before broadcasting.",
+ annotations: { title: "List segments", readOnlyHint: true },
+ },
+ handler(async () => api.get("/v1/segments")),
+ );
+
+ server.registerTool(
+ "list_scheduled",
+ {
+ title: "List scheduled sends",
+ description:
+ "List notifications queued for future delivery โ those given a `sendAt`, plus any deferred by a " +
+ "recipient's quiet hours. Use this to confirm a scheduled send landed, or to review what is " +
+ "pending before scheduling more.",
+ annotations: { title: "List scheduled sends", readOnlyHint: true },
+ },
+ handler(async () => api.get("/v1/notifications/scheduled")),
+ );
+
+ server.registerTool(
+ "get_delivery_logs",
+ {
+ title: "Get delivery logs",
+ description:
+ "Query the durable delivery log: what was sent, on which channel, and whether it succeeded. Call " +
+ "this to answer 'did it go out?' after a send, or to investigate failures. Filter by template, " +
+ "channel, status, or workflow instance. Results are paginated โ pass the returned `nextCursor` " +
+ "back as `cursor` for the next page.",
+ inputSchema: {
+ limit: z
+ .number()
+ .int()
+ .min(1)
+ .max(200)
+ .optional()
+ .describe("Rows per page. Defaults to the server's page size."),
+ cursor: z
+ .string()
+ .optional()
+ .describe("Pagination cursor from a previous call's `nextCursor`."),
+ templateId: z.string().optional().describe("Only log entries rendered from this template."),
+ workflowInstanceId: z
+ .string()
+ .optional()
+ .describe("Only entries emitted by this workflow instance."),
+ channel: z.enum(CHANNELS).optional().describe("Only entries delivered on this channel."),
+ status: z
+ .string()
+ .optional()
+ .describe("Delivery status to filter on, e.g. 'sent' or 'failed'."),
+ },
+ annotations: { title: "Get delivery logs", readOnlyHint: true },
+ },
+ handler(async (args) => api.get("/v1/notifications/logs", args)),
+ );
+
+ // โโโ Templates โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+ server.registerTool(
+ "list_templates",
+ {
+ title: "List templates",
+ description:
+ "List every registered template with its channel and content. Call this to find the template id " +
+ "for send_notification, or to check what a template renders before sending with it.",
+ annotations: { title: "List templates", readOnlyHint: true },
+ },
+ handler(async () => api.get("/v1/templates")),
+ );
+
+ server.registerTool(
+ "get_template",
+ {
+ title: "Get a template",
+ description:
+ "Fetch one template by id, including its full per-channel content and any AI prompts.",
+ inputSchema: { id: z.string().min(1).describe("Template id.") },
+ annotations: { title: "Get a template", readOnlyHint: true },
+ },
+ handler(async (args) => api.get(`/v1/templates/${encodeURIComponent(args.id)}`)),
+ );
+
+ server.registerTool(
+ "upsert_template",
+ {
+ title: "Create or update a template",
+ description:
+ "Create a template, or overwrite an existing one with the same id. Content is a per-channel object " +
+ "whose values may contain {{variable}} placeholders filled by send_notification's `data`; escaping " +
+ "is decided by the destination field. Because this overwrites, call get_template first when " +
+ "editing one that already exists.",
+ inputSchema: {
+ id: z.string().min(1).describe("Template id, e.g. 'order-shipped'."),
+ channel: z.enum(CHANNELS).describe("Channel this template renders for."),
+ topic: z
+ .union([z.string().min(1), z.array(z.string().min(1))])
+ .optional()
+ .describe("Topic(s) this template belongs to, used for per-user topic opt-outs."),
+ content: z
+ .record(z.unknown())
+ .describe("Channel-specific fields, e.g. { subject, body } for email."),
+ aiPrompts: z
+ .record(z.string())
+ .optional()
+ .describe("Optional per-field LLM prompts applied before rendering."),
+ },
+ annotations: {
+ title: "Create or update a template",
+ readOnlyHint: false,
+ idempotentHint: true,
+ },
+ },
+ handler(async (args) => api.put("/v1/templates", { templates: [args] })),
+ );
+
+ // โโโ Users โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+ server.registerTool(
+ "list_users",
+ {
+ title: "List users",
+ description:
+ "Page through the project's users. Results are paginated โ pass the returned `nextCursor` back as " +
+ "`cursor`. For one known user prefer get_user, which also returns contacts and preferences.",
+ inputSchema: {
+ limit: z.number().int().min(1).max(200).optional().describe("Users per page."),
+ cursor: z
+ .string()
+ .optional()
+ .describe("Pagination cursor from a previous call's `nextCursor`."),
+ },
+ annotations: { title: "List users", readOnlyHint: true },
+ },
+ handler(async (args) => api.get("/v1/users", args)),
+ );
+
+ server.registerTool(
+ "get_user",
+ {
+ title: "Get a user",
+ description:
+ "Fetch one user with their contacts, segments, and notification preferences. Call this before " +
+ "sending to a single user when you need to know which channels they can actually be reached on.",
+ inputSchema: { id: z.string().min(1).describe("User id.") },
+ annotations: { title: "Get a user", readOnlyHint: true },
+ },
+ handler(async (args) => api.get(`/v1/users/${encodeURIComponent(args.id)}/details`)),
+ );
+
+ server.registerTool(
+ "upsert_user",
+ {
+ title: "Create or update a user",
+ description:
+ "Create a user profile, or update an existing one with the same id. Sets contact targets, segment " +
+ "tags, timezone, and preferences in one call. Segments assigned here are what send_notification's " +
+ "`segment` targeting matches on. Timezone drives quiet-hours maths, so set it when known.",
+ inputSchema: {
+ id: z.string().min(1).describe("Your own stable user id, e.g. 'usr_123'."),
+ email: z
+ .union([z.string().min(1), z.array(z.string().min(1))])
+ .optional()
+ .describe("One email address or several."),
+ phone: z
+ .union([z.string().min(1), z.array(z.string().min(1))])
+ .optional()
+ .describe("One phone number or several, in E.164 form."),
+ pushToken: z
+ .union([z.string().min(1), z.array(z.string().min(1))])
+ .optional()
+ .describe("One device push token or several."),
+ segments: z
+ .array(z.string().min(1))
+ .optional()
+ .describe("Segment tags to assign to this user."),
+ language: z
+ .string()
+ .optional()
+ .describe("BCP 47 language tag used to pick localised content."),
+ timezone: z
+ .string()
+ .optional()
+ .describe("IANA timezone, e.g. 'Europe/London'. Drives quiet hours."),
+ preferences: z
+ .object({
+ channels: z
+ .record(z.boolean())
+ .optional()
+ .describe("Per-channel opt-in map, e.g. { email: true, sms: false }."),
+ topics: z.record(z.boolean()).optional().describe("Per-topic opt-in map."),
+ quietHours: z
+ .array(
+ z.object({
+ start: z.string().describe("HH:MM, 24h, UTC."),
+ end: z.string().describe("HH:MM, 24h, UTC."),
+ }),
+ )
+ .optional()
+ .describe("Windows during which non-critical sends are deferred."),
+ })
+ .optional()
+ .describe("Consent and quiet-hours settings."),
+ },
+ annotations: { title: "Create or update a user", readOnlyHint: false, idempotentHint: true },
+ },
+ handler(async (args) => api.post("/v1/users", args)),
+ );
+
+ server.registerTool(
+ "add_user_contact",
+ {
+ title: "Add a contact to a user",
+ description:
+ "Add one addressable target (email address, phone number, push token, or webhook URL) to an " +
+ "existing user without replacing their other contacts. Prefer this over upsert_user when adding a " +
+ "second device or address to someone who already exists.",
+ inputSchema: {
+ userId: z.string().min(1).describe("Id of the user to add the contact to."),
+ channel: z.enum(CONTACT_CHANNELS).describe("Channel this target belongs to."),
+ target: z.string().min(1).describe("The address itself: email, phone, push token, or URL."),
+ },
+ annotations: { title: "Add a contact to a user", readOnlyHint: false },
+ },
+ handler(async ({ userId, ...body }) =>
+ api.post(`/v1/users/${encodeURIComponent(userId)}/contacts`, body),
+ ),
+ );
+
+ // โโโ Workflows โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+ server.registerTool(
+ "create_workflow",
+ {
+ title: "Create a workflow",
+ description:
+ "Define a named multi-step sequence โ the way to build a drip, an onboarding series, or a reminder " +
+ "chain rather than a single send. Steps run in order and are one of: `notify` (send, using the " +
+ "same fields as send_notification), `wait` (pause for a duration like '1h' or '3d'), or " +
+ "`waitForEvent` (block until ingest_event reports a named event, with an optional timeout). A " +
+ "notify step with no target inherits the user the instance was triggered for. Creating a workflow " +
+ "does not run it โ call trigger_workflow per user.",
+ inputSchema: {
+ name: z.string().min(1).describe("Workflow name, used later by trigger_workflow."),
+ steps: z
+ .array(
+ z.union([
+ z.object({
+ action: z.literal("notify"),
+ payload: z
+ .record(z.unknown())
+ .describe(
+ "Same shape as send_notification's arguments. Omit the target to use the instance's own user.",
+ ),
+ }),
+ z.object({
+ action: z.literal("wait"),
+ duration: z.string().describe("Duration string, e.g. '5m', '1h', '3d'."),
+ }),
+ z.object({
+ action: z.literal("waitForEvent"),
+ event: z.string().describe("Event name to wait for, as passed to ingest_event."),
+ options: z
+ .object({ timeout: z.string().optional().describe("e.g. '24h'.") })
+ .optional()
+ .describe("Give a timeout so the instance cannot wait forever."),
+ }),
+ ]),
+ )
+ .min(1)
+ .describe("Ordered steps."),
+ },
+ annotations: { title: "Create a workflow", readOnlyHint: false, idempotentHint: true },
+ },
+ handler(async (args) => api.post("/v1/workflows", args)),
+ );
+
+ server.registerTool(
+ "list_workflows",
+ {
+ title: "List workflows",
+ description:
+ "List registered workflow definitions and their steps. Call this to find the name to pass to " +
+ "trigger_workflow, or to check a sequence before triggering it.",
+ annotations: { title: "List workflows", readOnlyHint: true },
+ },
+ handler(async () => api.get("/v1/workflows")),
+ );
+
+ server.registerTool(
+ "trigger_workflow",
+ {
+ title: "Trigger a workflow",
+ description:
+ "Start one run of a registered workflow, optionally for a specific user, and return the instance " +
+ "id. Each call starts an independent instance, so triggering the same workflow twice for one user " +
+ "enrols them twice. Track the returned instance with get_workflow_run.",
+ inputSchema: {
+ name: z.string().min(1).describe("Name of the workflow to run."),
+ user: z
+ .string()
+ .min(1)
+ .optional()
+ .describe("User id this run is for. Notify steps default to them."),
+ input: z.record(z.unknown()).optional().describe("Data made available to the run's steps."),
+ },
+ annotations: { title: "Trigger a workflow", readOnlyHint: false, idempotentHint: false },
+ },
+ handler(async (args) => api.post("/v1/workflows/trigger", args)),
+ );
+
+ server.registerTool(
+ "get_workflow_run",
+ {
+ title: "Get a workflow run",
+ description:
+ "Fetch one workflow instance by id: which step it is on, its status, and what it is waiting for. " +
+ "Use this to check on a run started by trigger_workflow.",
+ inputSchema: { instanceId: z.string().min(1).describe("Workflow instance id.") },
+ annotations: { title: "Get a workflow run", readOnlyHint: true },
+ },
+ handler(async (args) =>
+ api.get(`/v1/workflows/instances/${encodeURIComponent(args.instanceId)}`),
+ ),
+ );
+
+ server.registerTool(
+ "cancel_workflow_run",
+ {
+ title: "Cancel a workflow run",
+ description:
+ "Stop a running or suspended workflow instance so its remaining steps never fire. This does not " +
+ "recall notifications already sent, and it cannot be undone โ confirm with the user before " +
+ "cancelling a run you did not start in this session.",
+ inputSchema: { instanceId: z.string().min(1).describe("Workflow instance id to cancel.") },
+ annotations: {
+ title: "Cancel a workflow run",
+ readOnlyHint: false,
+ destructiveHint: true,
+ idempotentHint: true,
+ },
+ },
+ handler(async (args) => {
+ await api.request("DELETE", `/v1/workflows/instances/${encodeURIComponent(args.instanceId)}`);
+ return `Cancelled workflow instance ${args.instanceId}.`;
+ }),
+ );
+
+ server.registerTool(
+ "ingest_event",
+ {
+ title: "Ingest an event",
+ description:
+ "Report that something happened in your product. Workflow instances parked on a matching " +
+ "`waitForEvent` step resume. Call this to unblock a waiting sequence, e.g. emitting 'order.paid' " +
+ "so an abandoned-cart workflow stops before its reminder fires.",
+ inputSchema: {
+ name: z.string().min(1).describe("Event name, matching a workflow's waitForEvent step."),
+ properties: z
+ .record(z.unknown())
+ .describe("Event payload. Include the user id the event is about."),
+ },
+ annotations: { title: "Ingest an event", readOnlyHint: false, idempotentHint: false },
+ },
+ handler(async (args) => api.post("/v1/events", args)),
+ );
+
+ // โโโ Operations โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+ server.registerTool(
+ "get_system_health",
+ {
+ title: "Get system health",
+ description:
+ "Report the health of the notifkit deployment: queue depth, worker state, and dependency status. " +
+ "Call this first when sends appear queued but never delivered, to tell an outage apart from a " +
+ "preference or quiet-hours filter.",
+ annotations: { title: "Get system health", readOnlyHint: true },
+ },
+ handler(async () => api.get("/v1/system/health")),
+ );
+
+ server.registerTool(
+ "get_dead_letters",
+ {
+ title: "Get dead-lettered messages",
+ description:
+ "List messages that exhausted their retries and landed in the dead-letter queue, with the error " +
+ "that put them there. Use this to investigate deliveries that failed permanently; " +
+ "replay_dead_letter puts one back on the queue.",
+ annotations: { title: "Get dead-lettered messages", readOnlyHint: true },
+ },
+ handler(async () => api.get("/v1/dlq")),
+ );
+
+ server.registerTool(
+ "replay_dead_letter",
+ {
+ title: "Replay a dead-lettered message",
+ description:
+ "Re-queue one dead-lettered message for delivery. Only replay after the cause of the original " +
+ "failure is fixed, or it will simply fail again โ check get_dead_letters for the recorded error " +
+ "first. Replaying sends a real notification to a real person.",
+ inputSchema: {
+ id: z.string().min(1).describe("Dead-letter entry id from get_dead_letters."),
+ },
+ annotations: {
+ title: "Replay a dead-lettered message",
+ readOnlyHint: false,
+ idempotentHint: false,
+ },
+ },
+ handler(async (args) => api.post("/v1/dlq/replay", { id: args.id })),
+ );
+}
diff --git a/packages/mcp/tsconfig.json b/packages/mcp/tsconfig.json
new file mode 100644
index 0000000..a013e0c
--- /dev/null
+++ b/packages/mcp/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "include": ["src/**/*"]
+}
diff --git a/packages/provider-console/LICENSE b/packages/provider-console/LICENSE
new file mode 100644
index 0000000..235464d
--- /dev/null
+++ b/packages/provider-console/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 devkitshq
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/provider-console/README.md b/packages/provider-console/README.md
new file mode 100644
index 0000000..08865f5
--- /dev/null
+++ b/packages/provider-console/README.md
@@ -0,0 +1,19 @@
+# @notifkit/provider-console
+
+Console transport for [notifkit](https://github.com/devkitshq/notifkit). Prints
+notifications to stdout instead of sending them, so you can exercise the whole
+pipeline โ routing, preferences, quiet hours, templates โ without a provider
+account or a real inbox.
+
+```bash
+npm install @notifkit/provider-console
+```
+
+```ts
+import { ConsoleTransport } from "@notifkit/provider-console";
+
+registry.register("email", new ConsoleTransport("email"));
+```
+
+Requires `notifkit` as a peer dependency. Documentation:
+[notifkit.dev/docs](https://notifkit.dev/docs/). MIT licensed.
diff --git a/packages/provider-console/package.json b/packages/provider-console/package.json
new file mode 100644
index 0000000..e733825
--- /dev/null
+++ b/packages/provider-console/package.json
@@ -0,0 +1,39 @@
+{
+ "name": "@notifkit/provider-console",
+ "version": "0.1.0",
+ "type": "module",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "files": [
+ "dist",
+ "README.md",
+ "LICENSE"
+ ],
+ "scripts": {
+ "build": "tsc",
+ "dev": "tsc --watch",
+ "typecheck": "tsc --noEmit"
+ },
+ "peerDependencies": {
+ "notifkit": "^0.1.0"
+ },
+ "devDependencies": {
+ "notifkit": "file:../../",
+ "typescript": "^5.8.3"
+ },
+ "description": "Console transport for notifkit โ prints notifications to stdout instead of sending them. For local development and tests.",
+ "license": "MIT",
+ "author": "devkitshq",
+ "homepage": "https://notifkit.dev",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/devkitshq/notifkit.git",
+ "directory": "packages/provider-console"
+ },
+ "bugs": {
+ "url": "https://github.com/devkitshq/notifkit/issues"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/packages/provider-console/src/index.ts b/packages/provider-console/src/index.ts
new file mode 100644
index 0000000..3287f9b
--- /dev/null
+++ b/packages/provider-console/src/index.ts
@@ -0,0 +1,55 @@
+import type {
+ Transport,
+ NotificationDispatchedPayload,
+ DeliveryResult,
+ NotificationChannel,
+ Logger,
+} from "notifkit";
+
+export interface ConsoleTransportOptions {
+ channel?: NotificationChannel;
+ logger?: Logger;
+ limits?: { limit: number; windowSeconds: number };
+}
+
+export class ConsoleTransport implements Transport {
+ readonly channel: NotificationChannel;
+ readonly limits?: { limit: number; windowSeconds: number };
+ private readonly logger?: Logger;
+
+ constructor({ channel = "push", logger, limits }: ConsoleTransportOptions = {}) {
+ this.channel = channel;
+ this.logger = logger;
+ this.limits = limits;
+ }
+
+ async send(task: NotificationDispatchedPayload): Promise {
+ const providerMessageId = `console-${Date.now()}`;
+
+ console.log(
+ [
+ "",
+ "โโโโโโโโโโโโโโโโโโ ๐ฒ PUSH NOTIFICATION โโโโโโโโโโโโโโโโโ",
+ `โ to token : ${task.destination}`,
+ `โ recipient: ${task.recipientId}`,
+ `โ priority : ${task.priority}`,
+ `โ content : ${JSON.stringify(task.renderedContent.content)}`,
+ `โ taskId : ${task.taskId}`,
+ "โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ",
+ "",
+ ].join("\n"),
+ );
+
+ this.logger?.info(
+ {
+ taskId: task.taskId,
+ channel: this.channel,
+ destination: task.destination,
+ providerMessageId,
+ },
+ "push delivered (console transport)",
+ );
+
+ return { success: true, providerMessageId };
+ }
+}
diff --git a/packages/provider-console/tsconfig.json b/packages/provider-console/tsconfig.json
new file mode 100644
index 0000000..96081b2
--- /dev/null
+++ b/packages/provider-console/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "compilerOptions": {
+ "outDir": "dist",
+ "rootDir": "src",
+ "module": "NodeNext",
+ "moduleResolution": "NodeNext",
+ "target": "ES2022",
+ "declaration": true,
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true
+ },
+ "include": ["src/**/*"]
+}
diff --git a/packages/provider-fcm/LICENSE b/packages/provider-fcm/LICENSE
new file mode 100644
index 0000000..235464d
--- /dev/null
+++ b/packages/provider-fcm/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 devkitshq
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/provider-fcm/README.md b/packages/provider-fcm/README.md
new file mode 100644
index 0000000..8345e9d
--- /dev/null
+++ b/packages/provider-fcm/README.md
@@ -0,0 +1,23 @@
+# @notifkit/provider-fcm
+
+Firebase Cloud Messaging push transport for
+[notifkit](https://github.com/devkitshq/notifkit). Delivers to FCM registration
+tokens and reports invalid ones back so notifkit can deactivate them.
+
+```bash
+npm install @notifkit/provider-fcm
+```
+
+```ts
+import { FcmTransport } from "@notifkit/provider-fcm";
+
+registry.register(
+ "push",
+ new FcmTransport({
+ serviceAccountJson: process.env.FCM_SERVICE_ACCOUNT_JSON!,
+ }),
+);
+```
+
+Requires `notifkit` as a peer dependency and a Firebase service account.
+Documentation: [notifkit.dev/docs](https://notifkit.dev/docs/). MIT licensed.
diff --git a/packages/provider-fcm/package.json b/packages/provider-fcm/package.json
new file mode 100644
index 0000000..41e40ff
--- /dev/null
+++ b/packages/provider-fcm/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "@notifkit/provider-fcm",
+ "version": "0.1.0",
+ "type": "module",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "files": [
+ "dist",
+ "README.md",
+ "LICENSE"
+ ],
+ "scripts": {
+ "build": "tsc",
+ "dev": "tsc --watch",
+ "typecheck": "tsc --noEmit"
+ },
+ "dependencies": {
+ "firebase-admin": "^12.0.0"
+ },
+ "peerDependencies": {
+ "notifkit": "^0.1.0"
+ },
+ "devDependencies": {
+ "notifkit": "file:../../",
+ "typescript": "^5.8.3"
+ },
+ "description": "Firebase Cloud Messaging push transport for notifkit.",
+ "license": "MIT",
+ "author": "devkitshq",
+ "homepage": "https://notifkit.dev",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/devkitshq/notifkit.git",
+ "directory": "packages/provider-fcm"
+ },
+ "bugs": {
+ "url": "https://github.com/devkitshq/notifkit/issues"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/packages/provider-fcm/src/index.ts b/packages/provider-fcm/src/index.ts
new file mode 100644
index 0000000..2038da8
--- /dev/null
+++ b/packages/provider-fcm/src/index.ts
@@ -0,0 +1,111 @@
+import type { DeliveryResult, Transport } from "notifkit";
+import type { NotificationDispatchedPayload, Logger } from "notifkit";
+import type { App } from "firebase-admin/app";
+import type { Messaging } from "firebase-admin/messaging";
+
+export interface FcmTransportOptions {
+ serviceAccountJson: string;
+ logger?: Logger;
+ limits?: { limit: number; windowSeconds: number };
+}
+
+export class FcmTransport implements Transport {
+ readonly channel = "push" as const;
+ readonly limits?: { limit: number; windowSeconds: number };
+
+ private readonly serviceAccountJson: string;
+ private readonly logger?: Logger;
+ private messaging: Messaging | null = null;
+
+ constructor({
+ serviceAccountJson,
+ logger,
+ limits = { limit: 1000, windowSeconds: 60 },
+ }: FcmTransportOptions) {
+ this.serviceAccountJson = serviceAccountJson;
+ this.logger = logger;
+ this.limits = limits;
+ }
+
+ private async getMessaging(): Promise {
+ if (this.messaging) return this.messaging;
+
+ const { initializeApp, getApp, cert } = await import("firebase-admin/app");
+ const { getMessaging } = await import("firebase-admin/messaging");
+
+ let app: App;
+ try {
+ app = getApp("notifkit");
+ } catch {
+ const serviceAccount = JSON.parse(this.serviceAccountJson) as Record;
+ app = initializeApp({ credential: cert(serviceAccount) }, "notifkit");
+ }
+
+ this.messaging = getMessaging(app);
+ return this.messaging;
+ }
+
+ async send(task: NotificationDispatchedPayload): Promise {
+ // `destination` is optional on the contract, so a malformed task can reach
+ // a transport without one. Failing here costs one clear error; passing
+ // `undefined` to FCM costs an opaque provider rejection and a retry cycle.
+ const token = task.destination;
+ if (!token) {
+ return { success: false, error: "No destination (FCM registration token) on task" };
+ }
+
+ const messaging = await this.getMessaging();
+ const content = task.renderedContent.content as any;
+ const body = content.text || content.body;
+ const subject = content.subject;
+ const htmlBody = content.htmlBody || content.html;
+
+ try {
+ const messageId = await messaging.send({
+ token,
+ notification: {
+ title: subject ?? "",
+ body,
+ },
+ data: {
+ eventId: task.enrichedEventId,
+ taskId: task.taskId,
+ ...(htmlBody ? { htmlBody } : {}),
+ },
+ android: {
+ priority: task.priority === "critical" || task.priority === "high" ? "high" : "normal",
+ },
+ apns: {
+ payload: {
+ aps: {
+ sound: "default",
+ badge: 1,
+ },
+ },
+ },
+ });
+
+ this.logger?.debug({ taskId: task.taskId, messageId }, "FCM message sent");
+ return { success: true, providerMessageId: messageId };
+ } catch (err) {
+ const error = err as { code?: string; message?: string };
+ const invalidTokenCodes = [
+ "messaging/invalid-registration-token",
+ "messaging/registration-token-not-registered",
+ "messaging/invalid-argument",
+ ];
+ const isInvalidToken = invalidTokenCodes.includes(error.code ?? "");
+
+ this.logger?.warn(
+ { taskId: task.taskId, code: error.code, invalidToken: isInvalidToken },
+ "FCM send failed",
+ );
+
+ return {
+ success: false,
+ invalidToken: isInvalidToken,
+ error: error.message ?? "FCM error",
+ };
+ }
+ }
+}
diff --git a/packages/provider-fcm/tsconfig.json b/packages/provider-fcm/tsconfig.json
new file mode 100644
index 0000000..a013e0c
--- /dev/null
+++ b/packages/provider-fcm/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "include": ["src/**/*"]
+}
diff --git a/packages/provider-resend/LICENSE b/packages/provider-resend/LICENSE
new file mode 100644
index 0000000..235464d
--- /dev/null
+++ b/packages/provider-resend/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 devkitshq
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/provider-resend/README.md b/packages/provider-resend/README.md
new file mode 100644
index 0000000..d5720eb
--- /dev/null
+++ b/packages/provider-resend/README.md
@@ -0,0 +1,26 @@
+# @notifkit/provider-resend
+
+Resend email transport for [notifkit](https://github.com/devkitshq/notifkit).
+Sends through Resend and verifies delivery webhooks with svix, so bounces and
+complaints feed back into notifkit's suppression list automatically.
+
+```bash
+npm install @notifkit/provider-resend
+```
+
+```ts
+import { ResendTransport } from "@notifkit/provider-resend";
+
+registry.register(
+ "email",
+ new ResendTransport({
+ apiKey: process.env.RESEND_API_KEY!,
+ from: "notifications@example.com",
+ webhookSecret: process.env.RESEND_WEBHOOK_SECRET,
+ }),
+);
+```
+
+A template may carry its own `from` and `replyTo`, so one transport can serve
+both transactional and campaign mail. Requires `notifkit` as a peer dependency.
+Documentation: [notifkit.dev/docs](https://notifkit.dev/docs/). MIT licensed.
diff --git a/packages/provider-resend/package.json b/packages/provider-resend/package.json
new file mode 100644
index 0000000..29d5bf8
--- /dev/null
+++ b/packages/provider-resend/package.json
@@ -0,0 +1,45 @@
+{
+ "name": "@notifkit/provider-resend",
+ "version": "0.1.0",
+ "type": "module",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "files": [
+ "dist",
+ "README.md",
+ "LICENSE"
+ ],
+ "scripts": {
+ "build": "tsc",
+ "dev": "tsc --watch",
+ "typecheck": "tsc --noEmit"
+ },
+ "dependencies": {
+ "react": "^19.2.7",
+ "react-dom": "^19.2.7",
+ "resend": "^3.0.0",
+ "svix": "^1.99.1"
+ },
+ "peerDependencies": {
+ "notifkit": "^0.1.0"
+ },
+ "devDependencies": {
+ "notifkit": "file:../../",
+ "typescript": "^5.8.3"
+ },
+ "description": "Resend email transport for notifkit, with svix-verified delivery webhooks.",
+ "license": "MIT",
+ "author": "devkitshq",
+ "homepage": "https://notifkit.dev",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/devkitshq/notifkit.git",
+ "directory": "packages/provider-resend"
+ },
+ "bugs": {
+ "url": "https://github.com/devkitshq/notifkit/issues"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/packages/provider-resend/src/index.ts b/packages/provider-resend/src/index.ts
new file mode 100644
index 0000000..b1caeef
--- /dev/null
+++ b/packages/provider-resend/src/index.ts
@@ -0,0 +1,234 @@
+import type { DeliveryResult, Transport } from "notifkit";
+import type { NotificationDispatchedPayload, Logger } from "notifkit";
+import type { WebhookEvent } from "notifkit";
+import { Resend } from "resend";
+import { Webhook } from "svix";
+
+export interface ResendTransportOptions {
+ apiKey: string;
+ /**
+ * Sender used for any template that does not name its own. A template may
+ * override it with a `from` in its content โ see `send`.
+ */
+ from: string;
+ logger?: Logger;
+ webhookSecret?: string;
+ limits?: { limit: number; windowSeconds: number };
+}
+
+export class ResendTransport implements Transport {
+ readonly channel = "email" as const;
+ readonly webhookPath = "/webhooks/resend";
+ readonly limits?: { limit: number; windowSeconds: number };
+
+ private readonly resend: Resend;
+ private readonly from: string;
+ private readonly logger?: Logger;
+ private readonly webhookSecret?: string;
+
+ constructor({
+ apiKey,
+ from,
+ logger,
+ webhookSecret,
+ limits = { limit: 1000, windowSeconds: 10 },
+ }: ResendTransportOptions) {
+ this.resend = new Resend(apiKey);
+ this.from = from;
+ this.logger = logger;
+ this.webhookSecret = webhookSecret;
+ this.limits = limits;
+ }
+
+ async send(task: NotificationDispatchedPayload): Promise {
+ // `destination` is optional on the contract, so a malformed task can reach
+ // a transport without one. Failing here costs one clear error; passing
+ // `undefined` to Resend costs an opaque provider rejection and a retry cycle.
+ const to = task.destination;
+ if (!to) {
+ return { success: false, error: "No destination (recipient address) on task" };
+ }
+
+ const content = task.renderedContent.content as any;
+ const body = content.text || content.body;
+ const subject = content.subject;
+ const htmlBody = content.htmlBody || content.html;
+
+ // A template may carry its own sender, so one transport can serve
+ // no-reply@ for receipts and marketing@ for campaigns. Both fields are
+ // header fields to the renderer, which strips CR/LF from interpolated
+ // values โ a template naming neither falls back to the constructor's.
+ // Non-strings are ignored rather than coerced: `from: 123` reaching the
+ // provider as "123" would fail the send with a confusing error.
+ const from = typeof content.from === "string" && content.from ? content.from : this.from;
+ const replyTo =
+ typeof content.replyTo === "string" && content.replyTo ? content.replyTo : undefined;
+
+ // Carries the List-Unsubscribe pair the engine attaches to topic-bearing
+ // mail. Passing them through is what makes the inbox render a real
+ // unsubscribe button instead of leaving the recipient the spam key.
+ const headers = task.deliveryOptions?.headers;
+
+ try {
+ const { data, error } = await this.resend.emails.send({
+ from,
+ to,
+ subject: subject ?? "Notification",
+ html: htmlBody ?? `${body} `,
+ text: body,
+ ...(replyTo ? { replyTo } : {}),
+ ...(headers && Object.keys(headers).length > 0 ? { headers } : {}),
+ });
+
+ if (error) {
+ this.logger?.warn({ taskId: task.taskId, error: error.message }, "Resend send failed");
+ return {
+ success: false,
+ error: error.message,
+ };
+ }
+
+ this.logger?.debug({ taskId: task.taskId, messageId: data?.id }, "Resend email sent");
+ return { success: true, providerMessageId: data?.id ?? "" };
+ } catch (err) {
+ const error = err as Error;
+ this.logger?.error({ taskId: task.taskId, error: error.message }, "Resend unexpected error");
+
+ return {
+ success: false,
+ error: error.message,
+ };
+ }
+ }
+
+ /**
+ * Check the svix signature and return the verified payload, or null.
+ *
+ * Fail closed. An unverified webhook is an anonymous, forgeable write into
+ * delivery history, so a missing secret or missing request data is a reason
+ * to drop the event โ never a reason to trust it.
+ */
+ private verifySignature(
+ rawBody: string | undefined,
+ headers: Record | undefined,
+ ): any | null {
+ if (!this.webhookSecret) {
+ this.logger?.error(
+ "Resend webhook rejected: webhookSecret is not configured, cannot verify signature",
+ );
+ return null;
+ }
+
+ if (!rawBody || !headers) {
+ this.logger?.warn(
+ "Resend webhook rejected: raw body or headers unavailable for verification",
+ );
+ return null;
+ }
+
+ const svixHeaders: Record = {};
+ for (const [key, val] of Object.entries(headers)) {
+ if (Array.isArray(val)) svixHeaders[key] = val.join(",");
+ else if (val) svixHeaders[key] = val;
+ }
+
+ try {
+ return new Webhook(this.webhookSecret).verify(rawBody, svixHeaders);
+ } catch (err: any) {
+ this.logger?.warn({ error: err.message }, "Resend webhook signature verification failed");
+ return null;
+ }
+ }
+
+ /**
+ * Signature gate for the route the API mounts at `webhookPath`.
+ *
+ * Required, not optional: the mounted route answers 501 for a transport that
+ * omits it, so without this `parseWebhook` is never reached at all.
+ */
+ verifyWebhook(rawBody: string, headers: Record): boolean {
+ return this.verifySignature(rawBody, headers) !== null;
+ }
+
+ async parseWebhook(
+ body: any,
+ rawBody?: string,
+ headers?: Record,
+ ): Promise {
+ // Verify again rather than trusting the caller's parse of the body. This
+ // method is public and may be called directly, and the payload svix returns
+ // is the only one worth acting on.
+ const verified = this.verifySignature(rawBody, headers);
+ if (verified === null) return [];
+ body = verified;
+
+ this.logger?.debug(
+ { bodyType: body?.type, bodyId: body?.data?.email_id },
+ "Resend webhook received",
+ );
+
+ if (!body || !body.type || !body.data || !body.data.email_id) {
+ this.logger?.warn("Resend webhook payload malformed or missing email_id");
+ return [];
+ }
+
+ let status: WebhookEvent["status"] | null = null;
+ switch (body.type) {
+ case "email.opened":
+ status = "opened";
+ break;
+ case "email.clicked":
+ status = "clicked";
+ break;
+ case "email.bounced":
+ status = "bounced";
+ break;
+ case "email.complained":
+ status = "complained";
+ break;
+ case "email.delivery_delayed":
+ case "email.delivered":
+ default:
+ this.logger?.debug({ type: body.type }, "Resend webhook ignored (untracked event type)");
+ return [];
+ }
+
+ if (status) {
+ this.logger?.info(
+ { providerMessageId: body.data.email_id, status, type: body.type },
+ "Resend webhook mapped to notifkit status",
+ );
+
+ // `to` is an array; a bounce or complaint concerns the first recipient.
+ const recipient = Array.isArray(body.data.to) ? body.data.to[0] : body.data.to;
+
+ // Resend reports bounce severity under `bounce.type`, using its own
+ // vocabulary. Anything not explicitly permanent is treated as soft, so an
+ // unrecognised value never suppresses a working address.
+ const rawBounceType: string | undefined = body.data.bounce?.type ?? body.data.bounce_type;
+ const bounceType =
+ status === "bounced"
+ ? /permanent|hard/i.test(rawBounceType ?? "")
+ ? ("hard" as const)
+ : ("soft" as const)
+ : undefined;
+
+ const metadata: Record = {};
+ if (status === "clicked" && body.data.click?.link) metadata["url"] = body.data.click.link;
+ if (rawBounceType) metadata["bounceType"] = rawBounceType;
+ if (body.data.bounce?.message) metadata["bounceMessage"] = body.data.bounce.message;
+
+ return [
+ {
+ providerMessageId: body.data.email_id,
+ status,
+ timestamp: body.created_at ? new Date(body.created_at) : new Date(),
+ ...(recipient ? { recipient } : {}),
+ ...(bounceType ? { bounceType } : {}),
+ ...(Object.keys(metadata).length > 0 ? { metadata } : {}),
+ },
+ ];
+ }
+ return [];
+ }
+}
diff --git a/packages/provider-resend/tsconfig.json b/packages/provider-resend/tsconfig.json
new file mode 100644
index 0000000..a013e0c
--- /dev/null
+++ b/packages/provider-resend/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "outDir": "dist",
+ "rootDir": "src"
+ },
+ "include": ["src/**/*"]
+}
diff --git a/scripts/build.sh b/scripts/build.sh
new file mode 100644
index 0000000..584604d
--- /dev/null
+++ b/scripts/build.sh
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+npm run build
diff --git a/scripts/setup.sh b/scripts/setup.sh
new file mode 100644
index 0000000..6d4d747
--- /dev/null
+++ b/scripts/setup.sh
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+node_version=$(node --version | cut -c2-)
+required_version="22.0.0"
+
+if [ "$(printf '%s\n' "$required_version" "$node_version" | sort -V | head -n1)" != "$required_version" ]; then
+ echo "Node.js >= 22.0.0 is required. Found: $node_version"
+ exit 1
+fi
+
+if ! command -v npm &>/dev/null; then
+ echo "npm is required."
+ exit 1
+fi
+
+npm install
+npm run build
diff --git a/scripts/test-push.sh b/scripts/test-push.sh
new file mode 100644
index 0000000..c9244e5
--- /dev/null
+++ b/scripts/test-push.sh
@@ -0,0 +1,90 @@
+#!/usr/bin/env bash
+#
+# End-to-end push notification test.
+#
+# Drives the full pipeline: API โ INBOUND โ enricher โ ENGINE โ OUTBOUND โ
+# delivery โ push transport. By default the delivery service uses the *console*
+# push transport (set in apps/delivery/.env or PUSH_TRANSPORT=console), so you
+# can SEE the rendered push printed with no Firebase / real device needed.
+#
+# To send a REAL push instead: set FIREBASE_SERVICE_ACCOUNT_JSON and
+# PUSH_TRANSPORT=fcm in apps/delivery/.env, and use a real FCM device token
+# below (DEVICE_TOKEN env var).
+#
+# Usage:
+# ./scripts/test-push.sh # console push (no setup)
+# DEVICE_TOKEN= ./scripts/test-push.sh # real token
+#
+set -euo pipefail
+
+API="${API:-http://localhost:3000}"
+PGURL="${DATABASE_URL:-postgres://platform:platform@localhost:5432/notifkit}"
+DELIVERY_LOG="${DELIVERY_LOG:-/tmp/notifkit/delivery.log}"
+
+# Unique per run so the engine's 3/hour throttle never blocks repeated tests.
+STAMP="$(date +%s)"
+USER_ID="test_push_${STAMP}"
+DEVICE_TOKEN="${DEVICE_TOKEN:-demo-fcm-token-${STAMP}}"
+NOTIF_ID="$(uuidgen | tr '[:upper:]' '[:lower:]')"
+
+say() { printf "\n\033[1;36mโถ %s\033[0m\n" "$1"; }
+fail() { printf "\n\033[1;31mโ %s\033[0m\n" "$1"; exit 1; }
+
+# โโ 0. Preflight: API must be up โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+say "Checking API health ($API)"
+if ! curl -sf "$API/health" >/dev/null; then
+ fail "API not reachable. Start the stack first: make build && make start"
+fi
+curl -s "$API/health"; echo
+
+# โโ 1. Create the recipient user โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+say "Creating user $USER_ID"
+curl -s -X POST "$API/v1/users" -H 'Content-Type: application/json' \
+ -d "{\"id\":\"$USER_ID\",\"email\":\"$USER_ID@example.com\"}"; echo
+
+# โโ 2. Register an active push device token โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+# Delivery resolves push targets from the `devices` table, so seed one there.
+say "Registering push device token: $DEVICE_TOKEN"
+psql "$PGURL" -q -c \
+ "INSERT INTO devices (user_id, device_token, platform, active)
+ VALUES ('$USER_ID', '$DEVICE_TOKEN', 'fcm', TRUE)
+ ON CONFLICT (device_token) DO UPDATE SET active = TRUE;"
+echo " ok"
+
+# โโ 3. Send a push notification through the pipeline โโโโโโโโโโโโโโโโโโโโโโโโโ
+say "Publishing push notification (id=$NOTIF_ID)"
+curl -s -X POST "$API/v1/notifications" -H 'Content-Type: application/json' -d "{
+ \"id\": \"$NOTIF_ID\",
+ \"recipientId\": \"$USER_ID\",
+ \"channel\": \"push\",
+ \"priority\": \"high\",
+ \"payload\": { \"title\": \"Hello from Notifkit\", \"body\": \"Your test push is on its way\" }
+}"; echo
+
+# โโ 4. Watch it come out the far end โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+say "Waiting for delivery (tailing $DELIVERY_LOG)โฆ"
+if [[ ! -f "$DELIVERY_LOG" ]]; then
+ printf "\033[1;33m! %s\033[0m\n" "Delivery log not found at $DELIVERY_LOG."
+ printf " Watch the delivery service output directly, or set DELIVERY_LOG=.\n"
+ exit 0
+fi
+
+# Each worker stage adds a few seconds of poll latency, so allow ~45s total.
+for _ in $(seq 1 45); do
+ if grep -q "$USER_ID" "$DELIVERY_LOG" 2>/dev/null; then
+ sleep 1 # let the full box finish printing
+ break
+ fi
+ sleep 1
+done
+
+echo "โโ delivery output โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ"
+grep -A9 -e "PUSH NOTIFICATION" -e "$USER_ID" "$DELIVERY_LOG" | tail -25 || true
+echo "โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ"
+
+if grep -q "no transport registered" "$DELIVERY_LOG"; then
+ printf "\n\033[1;33m! Push was dropped โ no push transport registered.\033[0m\n"
+ printf " Set PUSH_TRANSPORT=console in apps/delivery/.env and restart delivery.\n"
+fi
+
+say "Done. User=$USER_ID token=$DEVICE_TOKEN notif=$NOTIF_ID"
diff --git a/setup-dev.sh b/setup-dev.sh
new file mode 100755
index 0000000..e78e313
--- /dev/null
+++ b/setup-dev.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+# Exit immediately if a command exits with a non-zero status
+set -e
+
+echo "๐ Step 1: Installing workspace dependencies..."
+npm install
+
+echo "๐ ๏ธ Step 2: Compiling core library and transport packages..."
+npm run build
+
+echo "๐ Step 3: Running TypeScript compiler validation..."
+npm run typecheck
+
+echo "๐งช Step 4: Running integration and unit tests..."
+npm run test -- --run
+
+echo "โ
Setup complete! All systems operational, built, and tested."
diff --git a/src/client.ts b/src/client.ts
new file mode 100644
index 0000000..5505d30
--- /dev/null
+++ b/src/client.ts
@@ -0,0 +1,362 @@
+import type {
+ AddUserInput,
+ UpdateUserInput,
+ AddContactInput,
+ SyncTemplatesInput,
+ NotifyRequestInput,
+ TriggerWorkflowInput,
+ CreateWorkflowInput,
+ IngestEventInput,
+ UpdateProjectInput,
+} from "./contracts/sdk.js";
+
+export interface NotifkitClientOptions {
+ baseUrl: string;
+ headers?: Record;
+ templates?: SyncTemplatesInput["templates"];
+ apiKey?: string;
+}
+
+export class NotifkitClient {
+ private readonly options: NotifkitClientOptions;
+ private readonly baseUrl: string;
+ private readonly headers: Record;
+
+ constructor(options: NotifkitClientOptions) {
+ this.options = options;
+ this.baseUrl = options.baseUrl.replace(/\/$/, "");
+ this.headers = {
+ "Content-Type": "application/json",
+ ...(options.apiKey ? { Authorization: `Bearer ${options.apiKey}` } : {}),
+ ...options.headers,
+ };
+ }
+
+ private async request(path: string, method: string, body?: unknown): Promise {
+ const url = `${this.baseUrl}${path}`;
+ const res = await fetch(url, {
+ method,
+ headers: this.headers,
+ body: body ? JSON.stringify(body) : undefined,
+ });
+
+ if (res.status === 204) {
+ return undefined as T;
+ }
+
+ const data = await res.json();
+ if (!res.ok) {
+ const errorMsg =
+ (data as any).message || (data as any).error || `Request failed with status ${res.status}`;
+ throw new Error(errorMsg);
+ }
+ return data as T;
+ }
+
+ /** Sync templates with the server. */
+ async syncTemplates(input: SyncTemplatesInput): Promise<{ synced: number }> {
+ return this.request("/v1/templates", "PUT", input);
+ }
+
+ /** Create/upsert a user profile and contacts. */
+ async addUser(input: AddUserInput): Promise<{ id: string }> {
+ return this.request("/v1/users", "POST", input);
+ }
+
+ /** Update user profile. */
+ async updateUser(id: string, input: UpdateUserInput): Promise<{ id: string }> {
+ return this.request(`/v1/users/${id}`, "PATCH", input);
+ }
+
+ /** Delete user profile. */
+ async deleteUser(id: string): Promise {
+ return this.request(`/v1/users/${id}`, "DELETE");
+ }
+
+ /** Add contact targets to user profile. */
+ async addContact(
+ userId: string,
+ input: AddContactInput,
+ ): Promise<{ userId: string; channel: string; target: string }> {
+ return this.request(`/v1/users/${userId}/contacts`, "POST", input);
+ }
+
+ /** Delete a specific contact channel target. */
+ async deleteContact(userId: string, channel: string, target: string): Promise {
+ return this.request(`/v1/users/${userId}/contacts/${channel}/${target}`, "DELETE");
+ }
+
+ /** Request a notification dispatch. */
+ async notify(
+ input: NotifyRequestInput,
+ ): Promise<{ messageId: string; notificationId: string; target: unknown }> {
+ return this.request("/v1/notify", "POST", input);
+ }
+
+ /** Trigger a registered background workflow. */
+ async triggerWorkflow(
+ input: TriggerWorkflowInput,
+ ): Promise<{ messageId: string; instanceId: string }> {
+ return this.request("/v1/workflows/trigger", "POST", input);
+ }
+
+ /** Create a dynamic JSON workflow definition. */
+ async createWorkflow(input: CreateWorkflowInput): Promise<{ name: string }> {
+ return this.request("/v1/workflows", "POST", input);
+ }
+
+ /** Ingest an external event into the system to resume workflows or trigger automations. */
+ async ingestEvent(input: IngestEventInput): Promise<{ messageId: string; eventId: string }> {
+ return this.request("/v1/events", "POST", input);
+ }
+
+ /** Sync templates configured on the client options to the server. */
+ async sync(): Promise<{ synced: number }> {
+ if (!this.options.templates || this.options.templates.length === 0) {
+ return { synced: 0 };
+ }
+ return this.syncTemplates({ templates: this.options.templates });
+ }
+
+ // โโโ Missing Endpoints additions โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+ /** List registered workflow definitions. */
+ async listWorkflows(): Promise<{ workflows: any[] }> {
+ return this.request("/v1/workflows", "GET");
+ }
+
+ /** Get a workflow instance by ID. */
+ async getWorkflow(instanceId: string): Promise {
+ return this.request(`/v1/workflows/instances/${instanceId}`, "GET");
+ }
+
+ /** Cancel a running/suspended workflow instance. */
+ async cancelWorkflow(instanceId: string): Promise {
+ return this.request(`/v1/workflows/instances/${instanceId}`, "DELETE");
+ }
+
+ /** Get notification logs for the project. */
+ async getNotificationLogs(options?: {
+ limit?: number;
+ cursor?: string;
+ templateId?: string;
+ workflowInstanceId?: string;
+ channel?: string;
+ status?: string;
+ }): Promise<{ logs: any[]; nextCursor: string | null }> {
+ let url = "/v1/notifications/logs";
+ if (options) {
+ const params = new URLSearchParams();
+ if (options.limit !== undefined) params.append("limit", options.limit.toString());
+ if (options.cursor) params.append("cursor", options.cursor);
+ if (options.templateId) params.append("templateId", options.templateId);
+ if (options.workflowInstanceId)
+ params.append("workflowInstanceId", options.workflowInstanceId);
+ if (options.channel) params.append("channel", options.channel);
+ if (options.status) params.append("status", options.status);
+ const str = params.toString();
+ if (str) url += `?${str}`;
+ }
+ return this.request(url, "GET");
+ }
+
+ /** List/paginate users. */
+ async listUsers(options?: {
+ limit?: number;
+ cursor?: string;
+ }): Promise<{ users: any[]; nextCursor: string | null }> {
+ const params = new URLSearchParams();
+ if (options?.limit) params.set("limit", options.limit.toString());
+ if (options?.cursor) params.set("cursor", options.cursor);
+ const qs = params.toString();
+ return this.request(`/v1/users${qs ? `?${qs}` : ""}`, "GET");
+ }
+
+ /** Delete a template. */
+ async deleteTemplate(id: string): Promise {
+ return this.request(`/v1/templates/${id}`, "DELETE");
+ }
+
+ /** Get a user's contacts. */
+ async getUserContacts(userId: string): Promise<{ contacts: any[] }> {
+ return this.request(`/v1/users/${userId}/contacts`, "GET");
+ }
+
+ /** List projects (Admin only). */
+ async listProjects(): Promise<{ projects: any[] }> {
+ return this.request("/v1/projects", "GET");
+ }
+
+ /** Delete a project (Admin only). */
+ async deleteProject(id: string): Promise {
+ return this.request(`/v1/projects/${id}`, "DELETE");
+ }
+
+ /** Create a new project API key (Admin only). */
+ async createProjectKey(
+ id: string,
+ input?: { role?: "admin" | "read_only" },
+ ): Promise<{ id: string; apiKey: string; role: string }> {
+ return this.request(`/v1/projects/${id}/keys`, "POST", input || {});
+ }
+
+ /** List project API keys (Admin only). */
+ async listProjectKeys(id: string): Promise<{ keys: any[] }> {
+ return this.request(`/v1/projects/${id}/keys`, "GET");
+ }
+
+ /** Delete a project API key (Admin only). */
+ async deleteProjectKey(id: string, keyId: string): Promise {
+ return this.request(`/v1/projects/${id}/keys/${keyId}`, "DELETE");
+ }
+
+ /** Update project settings (Admin only). */
+ async updateProject(id: string, input: UpdateProjectInput): Promise<{ id: string }> {
+ return this.request(`/v1/projects/${id}`, "PATCH", input);
+ }
+
+ /** List unique segment tags. */
+ async listSegments(): Promise<{ segments: string[] }> {
+ return this.request("/v1/segments", "GET");
+ }
+
+ // โโโ Campaigns โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+ /** List campaign labels seen in the delivery log, most recent activity first. */
+ async listCampaigns(options?: { limit?: number }): Promise<{
+ campaigns: {
+ campaign: string;
+ messages: number;
+ firstSentAt: string;
+ lastActivityAt: string;
+ }[];
+ }> {
+ const qs = options?.limit ? `?limit=${options.limit}` : "";
+ return this.request(`/v1/campaigns${qs}`, "GET");
+ }
+
+ /** Delivery and engagement funnel for one campaign. */
+ async getCampaignStats(campaign: string): Promise<{
+ campaign: string;
+ totals: Record;
+ byChannel: Record>;
+ engagementTracked: boolean;
+ warnings: string[];
+ }> {
+ return this.request(`/v1/campaigns/${encodeURIComponent(campaign)}/stats`, "GET");
+ }
+
+ // โโโ Suppressions โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+ /** List suppressed destinations. */
+ async listSuppressions(options?: {
+ limit?: number;
+ channel?: string;
+ reason?: string;
+ }): Promise<{ suppressions: any[] }> {
+ const params = new URLSearchParams();
+ if (options?.limit) params.set("limit", options.limit.toString());
+ if (options?.channel) params.set("channel", options.channel);
+ if (options?.reason) params.set("reason", options.reason);
+ const qs = params.toString();
+ return this.request(`/v1/suppressions${qs ? `?${qs}` : ""}`, "GET");
+ }
+
+ /** Suppress a destination by hand. */
+ async createSuppression(input: {
+ channel: string;
+ target: string;
+ reason?: "unsubscribed" | "complained" | "bounced" | "manual";
+ }): Promise<{ channel: string; target: string; reason: string }> {
+ return this.request("/v1/suppressions", "POST", input);
+ }
+
+ /** Remove a suppression, re-enabling sends to that destination. */
+ async deleteSuppression(channel: string, target: string): Promise {
+ return this.request(
+ `/v1/suppressions/${encodeURIComponent(channel)}/${encodeURIComponent(target)}`,
+ "DELETE",
+ );
+ }
+
+ // โโโ Notification Status & Cancellation โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+ /** Get real-time status and delivery logs for a specific notification task. */
+ async getNotificationStatus(taskId: string): Promise<{ status: string; logs: any[] }> {
+ return this.request(`/v1/notifications/${encodeURIComponent(taskId)}`, "GET");
+ }
+
+ /** Cancel a scheduled notification task. */
+ async cancelNotification(taskId: string): Promise<{ success: boolean }> {
+ return this.request(`/v1/notifications/${encodeURIComponent(taskId)}`, "DELETE");
+ }
+
+ /** List pending scheduled messages. */
+ async getScheduledMessages(): Promise<{ scheduled: any[] }> {
+ return this.request("/v1/notifications/scheduled", "GET");
+ }
+
+ // โโโ User Profile & Preferences โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+ /** Get user profile and contacts by ID. */
+ async getUser(id: string): Promise {
+ return this.request(`/v1/users/${encodeURIComponent(id)}`, "GET");
+ }
+
+ /** Get user details including contacts and recent message logs. */
+ async getUserDetails(id: string): Promise {
+ return this.request(`/v1/users/${encodeURIComponent(id)}/details`, "GET");
+ }
+
+ /** Get user preferences. */
+ async getUserPreferences(id: string): Promise {
+ return this.request(`/v1/users/${encodeURIComponent(id)}/preferences`, "GET");
+ }
+
+ /** Update user preferences. */
+ async updateUserPreferences(
+ id: string,
+ preferences: Record,
+ ): Promise<{ id: string; preferences: any }> {
+ return this.request(`/v1/users/${encodeURIComponent(id)}/preferences`, "PATCH", preferences);
+ }
+
+ // โโโ Templates Querying โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+ /** List all templates for the project. */
+ async listTemplates(): Promise<{ templates: any[] }> {
+ return this.request("/v1/templates", "GET");
+ }
+
+ /** Get a template by ID. */
+ async getTemplate(id: string): Promise {
+ return this.request(`/v1/templates/${encodeURIComponent(id)}`, "GET");
+ }
+
+ // โโโ System Health, Metrics & DLQ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+ /** Get system health and worker status. */
+ async getSystemHealth(): Promise {
+ return this.request("/v1/system/health", "GET");
+ }
+
+ /** Get system metrics and queue lengths. */
+ async getSystemMetrics(): Promise {
+ return this.request("/v1/system/metrics", "GET");
+ }
+
+ /** Get dead-letter queue messages. */
+ async getDLQMessages(): Promise<{ messages: any[] }> {
+ return this.request("/v1/dlq", "GET");
+ }
+
+ /** Replay a dead-letter queue message. */
+ async replayDLQMessage(id: string): Promise<{ success: boolean; replayedId: string }> {
+ return this.request("/v1/dlq/replay", "POST", { id });
+ }
+
+ /** Delete a dead-letter queue message. */
+ async deleteDLQMessage(id: string): Promise<{ success: boolean }> {
+ return this.request(`/v1/dlq/${encodeURIComponent(id)}`, "DELETE");
+ }
+}
diff --git a/src/config/index.ts b/src/config/index.ts
new file mode 100644
index 0000000..c6c4bfe
--- /dev/null
+++ b/src/config/index.ts
@@ -0,0 +1,107 @@
+import { config } from "dotenv";
+import { resolve } from "node:path";
+import { z, type ZodTypeAny } from "zod";
+import type { LanguageModel } from "ai";
+
+import { ValidationError } from "@/index.js";
+
+export function loadEnv(path?: string): void {
+ const envPath = path ?? resolve(process.cwd(), ".env");
+ config({ path: envPath, override: false });
+}
+
+export function parseConfig(
+ schema: TSchema,
+ data: unknown,
+): z.output {
+ const result = schema.safeParse(data);
+
+ if (!result.success) {
+ const fields: Record = {};
+
+ for (const issue of result.error.issues) {
+ const key = issue.path.join(".");
+ fields[key] ??= [];
+ fields[key].push(issue.message);
+ }
+
+ throw new ValidationError("Configuration validation failed", fields);
+ }
+
+ return result.data;
+}
+
+export const baseConfigSchema = z.object({
+ NODE_ENV: z.enum(["development", "test", "production"]).default("development"),
+ LOG_LEVEL: z.enum(["fatal", "error", "warn", "info", "debug", "trace", "silent"]).default("info"),
+ PORT: z.coerce.number().int().min(1).max(65_535).default(3000),
+ HOST: z.string().default("127.0.0.1"),
+ REDIS_URL: z.string().url().default("redis://localhost:6379"),
+ DATABASE_URL: z.string().url().default("postgres://platform:platform@localhost:5432/notifkit"),
+ ADMIN_API_KEY: z.string().optional(),
+ WORKER_CONCURRENCY: z.coerce.number().int().min(1).default(10),
+ QUEUE_MAX_LEN: z.coerce.number().int().min(1).default(10000000),
+ DB_MAX_CONNECTIONS: z.coerce.number().int().min(1).default(2),
+ LOG_FLUSH_INTERVAL_MS: z.coerce.number().int().min(50).default(500),
+ LOG_BUFFER_MAX_SIZE: z.coerce.number().int().min(100).default(5000),
+ SEGMENT_MAX_USERS: z.coerce.number().int().min(1).default(10000),
+ /**
+ * Externally reachable base URL of this API. Unsubscribe links are built from
+ * it, so it must be what an inbox can actually reach โ not `HOST`/`PORT`,
+ * which describe the bind address behind your proxy.
+ */
+ PUBLIC_URL: z.string().url().optional(),
+ /**
+ * Signing key for unsubscribe tokens. Rotating it invalidates every
+ * unsubscribe link already sitting in someone's inbox, so treat it as
+ * permanent: a dead link means the recipient reaches for the spam button
+ * instead, which costs far more than the key ever protected.
+ */
+ UNSUBSCRIBE_SECRET: z.string().min(16).optional(),
+});
+
+export type BaseConfig = z.infer;
+
+let globalConfig: BaseConfig | null = null;
+
+export function setGlobalConfig(config: BaseConfig) {
+ globalConfig = config;
+}
+
+export function readBaseConfig(data: NodeJS.ProcessEnv = process.env): BaseConfig {
+ if (globalConfig) {
+ return globalConfig;
+ }
+ return parseConfig(baseConfigSchema, data);
+}
+
+export interface RateLimitConfig {
+ limit: number;
+ windowSeconds: number;
+}
+
+export interface AiConfig {
+ aiModel?: LanguageModel;
+ /** Hard cap on generated tokens per prompt. Bounds cost and email size. */
+ maxOutputTokens?: number;
+ /** Wall-clock budget for a single generation before it is aborted. */
+ timeoutMs?: number;
+ /** Max prompts executed for one notification. */
+ maxPromptsPerNotification?: number;
+}
+
+export const AI_DEFAULTS = {
+ maxOutputTokens: 1_000,
+ timeoutMs: 30_000,
+ maxPromptsPerNotification: 5,
+} as const;
+
+let globalAiConfig: AiConfig = {};
+
+export function setAiConfig(config: AiConfig) {
+ globalAiConfig = config;
+}
+
+export function getAiConfig(): AiConfig {
+ return globalAiConfig;
+}
diff --git a/src/contracts/common.ts b/src/contracts/common.ts
new file mode 100644
index 0000000..8e18933
--- /dev/null
+++ b/src/contracts/common.ts
@@ -0,0 +1,18 @@
+import { z } from "zod";
+
+export const NotificationChannelSchema = z.enum(["email", "sms", "push", "webhook", "in-app"]);
+export type NotificationChannel = z.infer;
+
+export const NotificationPrioritySchema = z.enum(["low", "normal", "high", "critical"]);
+export type NotificationPriority = z.infer;
+
+export const NotificationStatusSchema = z.enum([
+ "pending",
+ "queued",
+ "processing",
+ "delivered",
+ "failed",
+ "bounced",
+ "suppressed",
+]);
+export type NotificationStatus = z.infer;
diff --git a/src/contracts/envelope.ts b/src/contracts/envelope.ts
new file mode 100644
index 0000000..c4207e1
--- /dev/null
+++ b/src/contracts/envelope.ts
@@ -0,0 +1,31 @@
+import { z } from "zod";
+import { EventMetadataSchema, type EventMetadata } from "./metadata.js";
+import type { EventPayloadMap, KnownEventType } from "./registry.js";
+
+/**
+ * Wire format written to Redis Streams. The payload field is an opaque record
+ * at the envelope level โ use registry.parsePayload() to get a typed payload.
+ */
+export const EventEnvelopeSchema = z.object({
+ id: z.string().uuid(),
+ type: z.string(),
+ timestamp: z.string().datetime(),
+ payload: z.record(z.string(), z.unknown()),
+ metadata: EventMetadataSchema,
+});
+export type EventEnvelope = z.infer;
+
+/** Typed envelope where the payload is strongly typed via EventPayloadMap. */
+export type TypedEventEnvelope = Omit<
+ EventEnvelope,
+ "type" | "payload"
+> & {
+ type: K;
+ payload: EventPayloadMap[K];
+};
+
+// Backward-compatible alias used by the queue / workers packages.
+export type StreamEvent = EventEnvelope;
+export const StreamEventSchema = EventEnvelopeSchema;
+export type StreamEventMetadata = EventMetadata;
+export const StreamEventMetadataSchema = EventMetadataSchema;
diff --git a/src/contracts/events/notification-ai-pending.ts b/src/contracts/events/notification-ai-pending.ts
new file mode 100644
index 0000000..d04ba6b
--- /dev/null
+++ b/src/contracts/events/notification-ai-pending.ts
@@ -0,0 +1,18 @@
+import { z } from "zod";
+import { NotificationChannelSchema, NotificationPrioritySchema } from "@/contracts/common.js";
+import { RecipientProfileSchema } from "./notification-enriched.js";
+
+export const NotificationAiPendingPayloadSchema = z.object({
+ projectId: z.string().uuid(),
+ enrichedEventId: z.string().uuid(),
+ recipientId: z.string().min(1),
+ channel: NotificationChannelSchema,
+ priority: NotificationPrioritySchema,
+ templateId: z.string().min(1).optional(),
+ templateVariables: z.record(z.string(), z.unknown()),
+ recipient: RecipientProfileSchema,
+ aiPrompts: z.record(z.string(), z.string()),
+ scheduledAt: z.string().datetime().optional(),
+ fallbackChain: z.array(NotificationChannelSchema).optional(),
+});
+export type NotificationAiPendingPayload = z.infer;
diff --git a/src/contracts/events/notification-canceled.ts b/src/contracts/events/notification-canceled.ts
new file mode 100644
index 0000000..91baed7
--- /dev/null
+++ b/src/contracts/events/notification-canceled.ts
@@ -0,0 +1,7 @@
+import { z } from "zod";
+
+export const NotificationCanceledPayloadSchema = z.object({
+ projectId: z.string().uuid(),
+ taskId: z.string().min(1),
+});
+export type NotificationCanceledPayload = z.infer;
diff --git a/src/contracts/events/notification-created.ts b/src/contracts/events/notification-created.ts
new file mode 100644
index 0000000..a19bf26
--- /dev/null
+++ b/src/contracts/events/notification-created.ts
@@ -0,0 +1,14 @@
+import { z } from "zod";
+import { NotificationChannelSchema, NotificationPrioritySchema } from "@/contracts/common.js";
+
+export const NotificationCreatedPayloadSchema = z.object({
+ projectId: z.string().uuid(),
+ recipientId: z.string().min(1),
+ channel: NotificationChannelSchema,
+ priority: NotificationPrioritySchema.default("normal"),
+ templateId: z.string().min(1).optional(),
+ payload: z.record(z.string(), z.unknown()),
+ scheduledAt: z.string().datetime().optional(),
+ idempotencyKey: z.string().optional(),
+});
+export type NotificationCreatedPayload = z.infer;
diff --git a/src/contracts/events/notification-delivered.ts b/src/contracts/events/notification-delivered.ts
new file mode 100644
index 0000000..c343f5a
--- /dev/null
+++ b/src/contracts/events/notification-delivered.ts
@@ -0,0 +1,17 @@
+import { z } from "zod";
+import { NotificationChannelSchema } from "@/contracts/common.js";
+
+export const NotificationDeliveredPayloadSchema = z.object({
+ projectId: z.string().uuid(),
+ taskId: z.string().min(1),
+ enrichedEventId: z.string().uuid(),
+ channel: NotificationChannelSchema,
+ deliveredAt: z.string().datetime(),
+ providerMessageId: z.string().optional(),
+ providerResponse: z.record(z.string(), z.unknown()).optional(),
+ templateId: z.string().uuid().optional(),
+ workflowInstanceId: z.string().uuid().optional(),
+ /** Campaign this message belongs to, carried from the originating request. */
+ campaignId: z.string().min(1).max(128).optional(),
+});
+export type NotificationDeliveredPayload = z.infer;
diff --git a/src/contracts/events/notification-dispatched.ts b/src/contracts/events/notification-dispatched.ts
new file mode 100644
index 0000000..ee23dd3
--- /dev/null
+++ b/src/contracts/events/notification-dispatched.ts
@@ -0,0 +1,45 @@
+import { z } from "zod";
+import { NotificationChannelSchema, NotificationPrioritySchema } from "@/contracts/common.js";
+import { RecipientProfileSchema } from "./notification-enriched.js";
+
+export const RenderedContentSchema = z.object({
+ content: z.record(z.string(), z.unknown()),
+ attachments: z
+ .array(
+ z.object({
+ name: z.string(),
+ contentType: z.string(),
+ url: z.string().url(),
+ }),
+ )
+ .optional(),
+});
+export type RenderedContent = z.infer;
+
+export const DeliveryOptionsSchema = z.object({
+ maxAttempts: z.number().int().positive().default(3),
+ timeoutMs: z.number().int().positive().default(10_000),
+ headers: z.record(z.string(), z.string()).optional(),
+});
+export type DeliveryOptions = z.infer;
+
+export const NotificationDispatchedPayloadSchema = z.object({
+ projectId: z.string().uuid(),
+ taskId: z.string().min(1),
+ enrichedEventId: z.string().uuid(),
+ recipientId: z.string().min(1),
+ channel: NotificationChannelSchema,
+ priority: NotificationPrioritySchema,
+ templateId: z.string().min(1).optional(),
+ templateVariables: z.record(z.string(), z.unknown()).default({}),
+ aiPrompts: z.record(z.string(), z.string()).optional(),
+ recipient: RecipientProfileSchema.optional(),
+ renderedContent: RenderedContentSchema,
+ destination: z.string().min(1).optional(),
+ deliveryOptions: DeliveryOptionsSchema,
+ fallbackChain: z.array(NotificationChannelSchema).optional(),
+ throttleAttemptCount: z.number().int().nonnegative().optional(),
+ /** Campaign this message belongs to, carried from the originating request. */
+ campaignId: z.string().min(1).max(128).optional(),
+});
+export type NotificationDispatchedPayload = z.infer;
diff --git a/src/contracts/events/notification-enriched.ts b/src/contracts/events/notification-enriched.ts
new file mode 100644
index 0000000..011b2e4
--- /dev/null
+++ b/src/contracts/events/notification-enriched.ts
@@ -0,0 +1,43 @@
+import { z } from "zod";
+import { NotificationChannelSchema, NotificationPrioritySchema } from "@/contracts/common.js";
+
+export const RecipientProfileSchema = z.object({
+ id: z.string(),
+ email: z.string().email().optional(),
+ phone: z.string().optional(),
+ webhook: z.string().url().optional(),
+ pushTokens: z.array(z.string()).optional(),
+ pushToken: z.string().optional(),
+ locale: z.string().default("en"),
+ timezone: z.string().default("UTC"),
+ preferences: z.object({
+ optedOut: z.boolean().default(false),
+ channels: z.array(NotificationChannelSchema).default([]),
+ quietHours: z
+ .array(
+ z.object({
+ start: z.string(),
+ end: z.string(),
+ }),
+ )
+ .optional(),
+ }),
+});
+export type RecipientProfile = z.infer;
+
+export const NotificationEnrichedPayloadSchema = z.object({
+ projectId: z.string().uuid(),
+ rawEventId: z.string().uuid(),
+ recipientId: z.string().min(1),
+ channel: NotificationChannelSchema,
+ priority: NotificationPrioritySchema,
+ templateId: z.string().min(1).optional(),
+ templateVariables: z.record(z.string(), z.unknown()),
+ recipient: RecipientProfileSchema,
+ aiPrompts: z.record(z.string(), z.string()).optional(),
+ scheduledAt: z.string().datetime().optional(),
+ fallbackChain: z.array(NotificationChannelSchema).optional(),
+ /** Campaign this message belongs to, carried from the originating request. */
+ campaignId: z.string().min(1).max(128).optional(),
+});
+export type NotificationEnrichedPayload = z.infer;
diff --git a/src/contracts/events/notification-failed.ts b/src/contracts/events/notification-failed.ts
new file mode 100644
index 0000000..075743f
--- /dev/null
+++ b/src/contracts/events/notification-failed.ts
@@ -0,0 +1,19 @@
+import { z } from "zod";
+import { NotificationChannelSchema } from "@/contracts/common.js";
+
+export const NotificationFailedPayloadSchema = z.object({
+ projectId: z.string().uuid(),
+ taskId: z.string().min(1),
+ enrichedEventId: z.string().uuid(),
+ channel: NotificationChannelSchema,
+ failureReason: z.string(),
+ failureCode: z.string(),
+ retryable: z.boolean(),
+ attempt: z.number().int().positive(),
+ providerResponse: z.record(z.string(), z.unknown()).optional(),
+ templateId: z.string().uuid().optional(),
+ workflowInstanceId: z.string().uuid().optional(),
+ /** Campaign this message belongs to, carried from the originating request. */
+ campaignId: z.string().min(1).max(128).optional(),
+});
+export type NotificationFailedPayload = z.infer;
diff --git a/src/contracts/events/notification-requested.ts b/src/contracts/events/notification-requested.ts
new file mode 100644
index 0000000..88a5930
--- /dev/null
+++ b/src/contracts/events/notification-requested.ts
@@ -0,0 +1,36 @@
+import { z } from "zod";
+import { NotificationChannelSchema, NotificationPrioritySchema } from "@/contracts/common.js";
+
+/**
+ * A high-level notification request as issued by the SDK's `notify()` call.
+ *
+ * Unlike `notification.created` (which targets a single resolved recipient),
+ * this event carries the *unresolved* target โ a user id, a segment, or a
+ * topic. A downstream resolver stage fans it out into one
+ * `notification.created` per matching recipient, applying preference filters.
+ */
+export const NotificationTargetSchema = z.discriminatedUnion("type", [
+ z.object({ type: z.literal("user"), userId: z.string().min(1) }),
+ z.object({ type: z.literal("segment"), segment: z.string().min(1) }),
+ z.object({ type: z.literal("topic"), topic: z.string().min(1) }),
+]);
+export type NotificationTarget = z.infer;
+
+export const NotificationRequestedPayloadSchema = z.object({
+ projectId: z.string().uuid(),
+ target: NotificationTargetSchema,
+ templateId: z.string().min(1),
+ priority: NotificationPrioritySchema.default("normal"),
+ channels: z.array(NotificationChannelSchema).nonempty().optional(),
+ data: z.record(z.string(), z.unknown()).default({}),
+ fallback: z.boolean().default(false),
+ aiPrompts: z.record(z.string(), z.string()).optional(),
+ scheduledAt: z.string().datetime().optional(),
+ idempotencyKey: z.string().optional(),
+ /**
+ * Groups every message this request fans out into, so the send can be
+ * reported on afterwards. Carried unchanged to the delivery log.
+ */
+ campaignId: z.string().min(1).max(128).optional(),
+});
+export type NotificationRequestedPayload = z.infer;
diff --git a/src/contracts/events/notification-scheduled.ts b/src/contracts/events/notification-scheduled.ts
new file mode 100644
index 0000000..ee17f9d
--- /dev/null
+++ b/src/contracts/events/notification-scheduled.ts
@@ -0,0 +1,9 @@
+import { z } from "zod";
+
+export const NotificationScheduledPayloadSchema = z.object({
+ projectId: z.string().uuid(),
+ enrichedEventId: z.string().uuid(),
+ taskId: z.string().min(1),
+ scheduledAt: z.string().datetime(),
+});
+export type NotificationScheduledPayload = z.infer;
diff --git a/src/contracts/events/notification-skipped.ts b/src/contracts/events/notification-skipped.ts
new file mode 100644
index 0000000..94c573a
--- /dev/null
+++ b/src/contracts/events/notification-skipped.ts
@@ -0,0 +1,9 @@
+import { z } from "zod";
+
+export const NotificationSkippedPayloadSchema = z.object({
+ projectId: z.string().uuid(),
+ eventId: z.string().uuid(),
+ recipientId: z.string(),
+ reason: z.string(),
+});
+export type NotificationSkippedPayload = z.infer;
diff --git a/src/contracts/helpers.ts b/src/contracts/helpers.ts
new file mode 100644
index 0000000..ef487ee
--- /dev/null
+++ b/src/contracts/helpers.ts
@@ -0,0 +1,21 @@
+import { randomUUID } from "node:crypto";
+import { type EventEnvelope } from "./envelope.js";
+
+// Legacy factory helpers โ backward compatible with the queue package's publish() API,
+// which accepts Omit.
+export function buildStreamEvent(
+ type: string,
+ payload: Record,
+ source: string,
+ traceId?: string,
+): Omit {
+ return {
+ type,
+ payload,
+ metadata: {
+ traceId: traceId ?? randomUUID(),
+ source,
+ retryCount: 0,
+ },
+ };
+}
diff --git a/src/contracts/index.ts b/src/contracts/index.ts
new file mode 100644
index 0000000..accd4e9
--- /dev/null
+++ b/src/contracts/index.ts
@@ -0,0 +1,46 @@
+// Core building blocks
+export * from "./common.js";
+export * from "./metadata.js";
+export * from "./registry.js";
+export * from "./envelope.js";
+export * from "./streams.js";
+export * from "./helpers.js";
+export * from "./sdk.js";
+
+// Event payload schemas and types
+export * from "./events/notification-requested.js";
+export * from "./events/notification-created.js";
+export * from "./events/notification-enriched.js";
+export * from "./events/notification-scheduled.js";
+export * from "./events/notification-dispatched.js";
+export * from "./events/notification-delivered.js";
+export * from "./events/notification-failed.js";
+export * from "./events/notification-skipped.js";
+export * from "./events/notification-canceled.js";
+export * from "./events/notification-ai-pending.js";
+
+// Register all built-in event schemas in the global registry.
+// This runs once at module load time; call registry.define() in your own
+// module to add new event types without modifying this file.
+import { registry } from "./registry.js";
+import { NotificationRequestedPayloadSchema } from "./events/notification-requested.js";
+import { NotificationCreatedPayloadSchema } from "./events/notification-created.js";
+import { NotificationEnrichedPayloadSchema } from "./events/notification-enriched.js";
+import { NotificationScheduledPayloadSchema } from "./events/notification-scheduled.js";
+import { NotificationDispatchedPayloadSchema } from "./events/notification-dispatched.js";
+import { NotificationDeliveredPayloadSchema } from "./events/notification-delivered.js";
+import { NotificationFailedPayloadSchema } from "./events/notification-failed.js";
+import { NotificationSkippedPayloadSchema } from "./events/notification-skipped.js";
+import { NotificationCanceledPayloadSchema } from "./events/notification-canceled.js";
+import { NotificationAiPendingPayloadSchema } from "./events/notification-ai-pending.js";
+
+registry.define("notification.requested", NotificationRequestedPayloadSchema);
+registry.define("notification.created", NotificationCreatedPayloadSchema);
+registry.define("notification.enriched", NotificationEnrichedPayloadSchema);
+registry.define("notification.scheduled", NotificationScheduledPayloadSchema);
+registry.define("notification.dispatched", NotificationDispatchedPayloadSchema);
+registry.define("notification.delivered", NotificationDeliveredPayloadSchema);
+registry.define("notification.failed", NotificationFailedPayloadSchema);
+registry.define("notification.skipped", NotificationSkippedPayloadSchema);
+registry.define("notification.canceled", NotificationCanceledPayloadSchema);
+registry.define("notification.ai_pending", NotificationAiPendingPayloadSchema);
diff --git a/src/contracts/metadata.ts b/src/contracts/metadata.ts
new file mode 100644
index 0000000..b0a004d
--- /dev/null
+++ b/src/contracts/metadata.ts
@@ -0,0 +1,10 @@
+import { z } from "zod";
+
+export const EventMetadataSchema = z.object({
+ traceId: z.string(),
+ source: z.string(),
+ retryCount: z.number().int().nonnegative().default(0),
+ correlationId: z.string().optional(),
+ causationId: z.string().optional(),
+});
+export type EventMetadata = z.infer;
diff --git a/src/contracts/registry.ts b/src/contracts/registry.ts
new file mode 100644
index 0000000..b4aaeef
--- /dev/null
+++ b/src/contracts/registry.ts
@@ -0,0 +1,88 @@
+import { z, type ZodTypeAny, type ZodError } from "zod";
+import type { NotificationRequestedPayload } from "./events/notification-requested.js";
+import type { NotificationCreatedPayload } from "./events/notification-created.js";
+import type { NotificationEnrichedPayload } from "./events/notification-enriched.js";
+import type { NotificationScheduledPayload } from "./events/notification-scheduled.js";
+import type { NotificationDispatchedPayload } from "./events/notification-dispatched.js";
+import type { NotificationDeliveredPayload } from "./events/notification-delivered.js";
+import type { NotificationFailedPayload } from "./events/notification-failed.js";
+import type { NotificationSkippedPayload } from "./events/notification-skipped.js";
+import type { NotificationCanceledPayload } from "./events/notification-canceled.js";
+import type { NotificationAiPendingPayload } from "./events/notification-ai-pending.js";
+/**
+ * Built-in event payload types.
+ * External packages extend this interface via declaration merging:
+ *
+ * declare module "../index.js" {
+ * interface EventPayloadMap {
+ * "my.custom.event": { field: string };
+ * }
+ * }
+ *
+ * Then call registry.define("my.custom.event", MySchema) at app startup.
+ */
+export interface EventPayloadMap {
+ "notification.requested": NotificationRequestedPayload;
+ "notification.created": NotificationCreatedPayload;
+ "notification.enriched": NotificationEnrichedPayload;
+ "notification.scheduled": NotificationScheduledPayload;
+ "notification.dispatched": NotificationDispatchedPayload;
+ "notification.delivered": NotificationDeliveredPayload;
+ "notification.failed": NotificationFailedPayload;
+ "notification.skipped": NotificationSkippedPayload;
+ "notification.canceled": NotificationCanceledPayload;
+ "notification.ai_pending": NotificationAiPendingPayload;
+}
+
+export type KnownEventType = keyof EventPayloadMap & string;
+
+export type ParseResult = { success: true; data: T } | { success: false; error: ZodError };
+
+export class EventRegistry {
+ private readonly schemas = new Map();
+
+ define(type: string, schema: ZodTypeAny): void {
+ if (this.schemas.has(type)) {
+ throw new Error(`Event type "${type}" is already registered`);
+ }
+ this.schemas.set(type, schema);
+ }
+
+ getSchema(type: string): ZodTypeAny | undefined {
+ return this.schemas.get(type);
+ }
+
+ has(type: string): boolean {
+ return this.schemas.has(type);
+ }
+
+ types(): string[] {
+ return [...this.schemas.keys()];
+ }
+
+ parsePayload(type: K, payload: unknown): EventPayloadMap[K] {
+ const schema = this.schemas.get(type);
+ if (!schema) throw new Error(`Unknown event type: "${type}"`);
+ return schema.parse(payload) as EventPayloadMap[K];
+ }
+
+ safeParsePayload(
+ type: K,
+ payload: unknown,
+ ): ParseResult {
+ const schema = this.schemas.get(type);
+ if (!schema) {
+ return {
+ success: false,
+ error: new z.ZodError([
+ { code: "custom", message: `Unknown event type: "${type}"`, path: [] },
+ ]),
+ };
+ }
+ const result = schema.safeParse(payload);
+ if (result.success) return { success: true, data: result.data as EventPayloadMap[K] };
+ return { success: false, error: result.error };
+ }
+}
+
+export const registry = new EventRegistry();
diff --git a/src/contracts/sdk.ts b/src/contracts/sdk.ts
new file mode 100644
index 0000000..c7178f9
--- /dev/null
+++ b/src/contracts/sdk.ts
@@ -0,0 +1,233 @@
+import { z } from "zod";
+import { NotificationChannelSchema, NotificationPrioritySchema } from "./common.js";
+
+// โโโ Preferences โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+//
+// Preferences are stored as JSONB and are intentionally open-ended so new
+// channels/topics can be added without a migration. `channels` and `topics`
+// are boolean opt-in maps; `quietHours` is a list of UTC HH:MM windows.
+
+export const QuietHoursSchema = z.object({
+ start: z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, "expected HH:MM (24h, UTC)"),
+ end: z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, "expected HH:MM (24h, UTC)"),
+});
+export type QuietHours = z.infer;
+
+export const PreferencesSchema = z.object({
+ channels: z.record(z.string(), z.boolean()).optional(),
+ topics: z.record(z.string(), z.boolean()).optional(),
+ quietHours: z.array(QuietHoursSchema).optional(),
+});
+export type Preferences = z.infer;
+
+// โโโ Contacts โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+/** Channels that carry an addressable target (email address, phone, push token, url). */
+export const ContactChannelSchema = z.enum(["email", "sms", "push", "webhook"]);
+export type ContactChannel = z.infer;
+
+/** Accept a single value or an array; always normalise to a non-empty array. */
+const stringOrArray = z
+ .union([z.string().min(1), z.array(z.string().min(1))])
+ .transform((v) => (Array.isArray(v) ? v : [v]));
+
+// โโโ Users โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+/**
+ * addUser({ id, email, phone, pushToken, segments, preferences })
+ * email / phone / pushToken accept a single string or an array.
+ */
+export const AddUserSchema = z.object({
+ id: z.string().min(1),
+ language: z.string().optional(),
+ timezone: z.string().optional(),
+ email: stringOrArray.optional(),
+ phone: stringOrArray.optional(),
+ pushToken: stringOrArray.optional(),
+ segments: z.array(z.string().min(1)).optional(),
+ preferences: PreferencesSchema.optional(),
+});
+export type AddUserInput = z.input;
+
+/** updateUser(id, patch) โ every field optional; id comes from the path. */
+export const UpdateUserSchema = z.object({
+ language: z.string().optional(),
+ timezone: z.string().optional(),
+ email: stringOrArray.optional(),
+ phone: stringOrArray.optional(),
+ pushToken: stringOrArray.optional(),
+ segments: z.array(z.string().min(1)).optional(),
+ preferences: PreferencesSchema.optional(),
+});
+export type UpdateUserInput = z.input;
+
+/** addUserContact(userId, channel, { target, preferences }) โ channel carried in body. */
+export const AddContactSchema = z.object({
+ channel: ContactChannelSchema,
+ target: z.string().min(1),
+ preferences: PreferencesSchema.optional(),
+});
+export type AddContactInput = z.infer;
+
+// โโโ Templates โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+export const TemplateSchema = z.object({
+ id: z.string().min(1),
+ channel: NotificationChannelSchema,
+ topic: z
+ .union([z.string().min(1), z.array(z.string().min(1))])
+ .transform((v) => (Array.isArray(v) ? v : [v]))
+ .optional(),
+ content: z.record(z.string(), z.unknown()),
+ aiPrompts: z.record(z.string(), z.string()).optional(),
+});
+export type TemplateInput = z.infer;
+
+export const SyncTemplatesSchema = z.object({
+ templates: z.array(TemplateSchema).min(1),
+});
+export type SyncTemplatesInput = z.infer;
+
+// โโโ notify() โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+/** Inline user object accepted by notify({ user: {...} }). */
+export const InlineUserSchema = z.object({
+ id: z.string().min(1),
+ language: z.string().optional(),
+ timezone: z.string().optional(),
+ email: stringOrArray.optional(),
+ phone: stringOrArray.optional(),
+ pushToken: stringOrArray.optional(),
+ segments: z.array(z.string().min(1)).optional(),
+ preferences: PreferencesSchema.optional(),
+});
+export type InlineUser = z.infer;
+
+/**
+ * notify(...) request body.
+ *
+ * Exactly one of `user` / `segment` / `topic` must be provided.
+ */
+/**
+ * The field set shared by `notify()` and a workflow's `notify` step. The two
+ * differ only in whether naming a target is mandatory, so the fields live here
+ * once and each schema layers its own target rule on top.
+ */
+const NotifyRequestFields = z.object({
+ user: z
+ .union([
+ z.string().min(1),
+ InlineUserSchema,
+ z.array(z.union([z.string().min(1), InlineUserSchema])).nonempty(),
+ ])
+ .optional(),
+ segment: z.string().min(1).optional(),
+ topic: z.string().min(1).optional(),
+ template: z.string().min(1),
+ data: z.record(z.string(), z.unknown()).optional(),
+ aiPrompts: z.record(z.string(), z.string()).optional(),
+ priority: NotificationPrioritySchema.optional(),
+ channels: z.array(NotificationChannelSchema).nonempty().optional(),
+ fallback: z.boolean().optional(),
+ sendAt: z.string().datetime().optional(),
+ /**
+ * A label grouping every message this call produces, so the send can be
+ * reported on later via `/v1/campaigns/:id/stats`. Free-form, but reusing one
+ * label across calls merges them into a single campaign โ which is either
+ * what you want (a send split into batches) or a reporting bug.
+ */
+ campaign: z.string().min(1).max(128).optional(),
+});
+
+function countTargets(val: { user?: unknown; segment?: unknown; topic?: unknown }): number {
+ return [val.user, val.segment, val.topic].filter((t) => t !== undefined).length;
+}
+
+export const NotifyRequestSchema = NotifyRequestFields.superRefine((val, ctx) => {
+ const targets = countTargets(val);
+ if (targets === 0) {
+ ctx.addIssue({
+ code: z.ZodIssueCode.custom,
+ message: "one of `user`, `segment`, or `topic` is required",
+ path: ["user"],
+ });
+ } else if (targets > 1) {
+ ctx.addIssue({
+ code: z.ZodIssueCode.custom,
+ message: "provide exactly one of `user`, `segment`, or `topic`",
+ path: ["user"],
+ });
+ }
+});
+export type NotifyRequestInput = z.input;
+
+/**
+ * The payload of a workflow `notify` step โ every field `notify()` takes.
+ *
+ * The one difference is that a target is optional here: a step naming none
+ * inherits the instance's own user, which is the ordinary case. Naming one
+ * overrides that, so a step can notify a different user, a segment, or a topic.
+ */
+export const WorkflowNotifyPayloadSchema = NotifyRequestFields.superRefine((val, ctx) => {
+ if (countTargets(val) > 1) {
+ ctx.addIssue({
+ code: z.ZodIssueCode.custom,
+ message: "provide at most one of `user`, `segment`, or `topic`",
+ path: ["user"],
+ });
+ }
+});
+export type WorkflowNotifyInput = z.input;
+
+// โโโ Workflows โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+export const TriggerWorkflowSchema = z.object({
+ name: z.string().min(1),
+ input: z.record(z.string(), z.unknown()).optional(),
+ user: z.union([z.string().min(1), InlineUserSchema]).optional(),
+});
+export type TriggerWorkflowInput = z.infer;
+
+// โโโ Events โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+export const IngestEventSchema = z.object({
+ name: z.string().min(1),
+ properties: z.record(z.string(), z.unknown()),
+});
+export type IngestEventInput = z.infer;
+
+export const WorkflowStepSchema = z.discriminatedUnion("action", [
+ z.object({
+ action: z.literal("notify"),
+ payload: WorkflowNotifyPayloadSchema,
+ }),
+ z.object({
+ action: z.literal("wait"),
+ duration: z.string(), // e.g. "1h", "5m"
+ }),
+ z.object({
+ action: z.literal("waitForEvent"),
+ event: z.string(),
+ options: z
+ .object({
+ timeout: z.string().optional(), // e.g. "24h"
+ })
+ .optional(),
+ }),
+]);
+export type WorkflowStepDef = z.infer;
+
+export const CreateWorkflowSchema = z.object({
+ name: z.string().min(1),
+ steps: z.array(WorkflowStepSchema).min(1),
+});
+export type CreateWorkflowInput = z.infer;
+
+// โโโ Projects โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+export const UpdateProjectSchema = z.object({
+ rateLimitRpm: z.number().nullable().optional(),
+ throttleLimit: z.number().nullable().optional(),
+ throttleWindowHours: z.number().nullable().optional(),
+});
+export type UpdateProjectInput = z.infer;
diff --git a/src/contracts/streams.ts b/src/contracts/streams.ts
new file mode 100644
index 0000000..28a5493
--- /dev/null
+++ b/src/contracts/streams.ts
@@ -0,0 +1,62 @@
+export const STREAMS = {
+ INBOUND_CRITICAL: "notifkit:stream:inbound:critical",
+ INBOUND_NORMAL: "notifkit:stream:inbound:normal",
+ INBOUND_LOW: "notifkit:stream:inbound:low",
+ ENRICHED_CRITICAL: "notifkit:stream:enriched:critical",
+ ENRICHED_NORMAL: "notifkit:stream:enriched:normal",
+ ENRICHED_LOW: "notifkit:stream:enriched:low",
+ AI_PENDING: "notifkit:stream:ai:pending",
+ SCHEDULED: "notifkit:stream:scheduled",
+ OUTBOUND_CRITICAL: "notifkit:stream:outbound:critical",
+ OUTBOUND_NORMAL: "notifkit:stream:outbound:normal",
+ OUTBOUND_LOW: "notifkit:stream:outbound:low",
+ DEAD_LETTER: "notifkit:stream:dlq",
+ WORKFLOW_INBOUND: "notifkit:stream:workflow:inbound",
+ EVENTS_INBOUND: "notifkit:stream:events:inbound",
+} as const;
+
+export const INBOUND_STREAMS = [
+ STREAMS.INBOUND_CRITICAL,
+ STREAMS.INBOUND_NORMAL,
+ STREAMS.INBOUND_LOW,
+] as const;
+
+export const ENRICHED_STREAMS = [
+ STREAMS.ENRICHED_CRITICAL,
+ STREAMS.ENRICHED_NORMAL,
+ STREAMS.ENRICHED_LOW,
+] as const;
+
+export const OUTBOUND_STREAMS = [
+ STREAMS.OUTBOUND_CRITICAL,
+ STREAMS.OUTBOUND_NORMAL,
+ STREAMS.OUTBOUND_LOW,
+] as const;
+export type StreamName = (typeof STREAMS)[keyof typeof STREAMS];
+
+/**
+ * Redis pub/sub channels used to drop cached state across every process.
+ *
+ * Each cache also carries a TTL, so these only shorten the window in which a
+ * worker can act on stale data โ they are not the sole correctness mechanism.
+ */
+export const PUBSUB_CHANNELS = {
+ /** Payload: `{projectId}:{templateId}`. */
+ TEMPLATE_INVALIDATED: "template.invalidated",
+ /** Payload: `{projectId}`. Published when project settings change. */
+ PROJECT_INVALIDATED: "project.invalidated",
+ /** Payload: a token hash, or `*` for the whole cache. */
+ API_KEY_INVALIDATED: "apikey.invalidated",
+} as const;
+export type PubSubChannel = (typeof PUBSUB_CHANNELS)[keyof typeof PUBSUB_CHANNELS];
+
+export const CONSUMER_GROUPS = {
+ ENRICHER: "notifkit:group:enricher",
+ ENGINE: "notifkit:group:engine",
+ DELIVERY: "notifkit:group:delivery",
+ SCHEDULER: "notifkit:group:scheduler",
+ AI: "notifkit:group:ai",
+ WORKFLOW: "notifkit:group:workflow",
+ EVENTS: "notifkit:group:events",
+} as const;
+export type ConsumerGroup = (typeof CONSUMER_GROUPS)[keyof typeof CONSUMER_GROUPS];
diff --git a/src/db/index.ts b/src/db/index.ts
new file mode 100644
index 0000000..6e1773a
--- /dev/null
+++ b/src/db/index.ts
@@ -0,0 +1,69 @@
+import postgres from "postgres";
+import { drizzle, type PostgresJsDatabase } from "drizzle-orm/postgres-js";
+import type { Logger } from "@/index.js";
+import * as schema from "./schema.js";
+import { readBaseConfig } from "@/index.js";
+
+export type Sql = postgres.Sql;
+export type Db = PostgresJsDatabase;
+
+export interface DatabaseOptions {
+ url: string;
+ applicationName?: string;
+ maxConnections?: number;
+ idleTimeoutSeconds?: number;
+ logger?: Logger;
+}
+
+export interface DatabaseClients {
+ sql: Sql;
+ db: Db;
+}
+
+/**
+ * Create a postgres.js connection pool and initialize Drizzle ORM.
+ * Call once at application startup; pass db into repositories.
+ * Call sql.end() during graceful shutdown.
+ */
+export function createDatabase({
+ url,
+ applicationName = "notifkit",
+ maxConnections,
+ idleTimeoutSeconds = 30,
+ logger,
+}: DatabaseOptions): DatabaseClients {
+ const finalMaxConnections = maxConnections ?? readBaseConfig().DB_MAX_CONNECTIONS;
+
+ const sql = postgres(url, {
+ max: finalMaxConnections,
+ idle_timeout: idleTimeoutSeconds,
+ connection: {
+ application_name: applicationName,
+ statement_timeout: 10000 as any, // prevent TS issues with postgres.js types
+ },
+ onnotice: (notice) => {
+ logger?.debug({ notice }, "postgres notice");
+ },
+ });
+
+ const db = drizzle(sql, { schema });
+
+ return { sql, db };
+}
+
+import { migrate } from "drizzle-orm/postgres-js/migrator";
+import { fileURLToPath } from "url";
+import path from "path";
+import fs from "fs";
+
+export async function runMigrations(db: Db) {
+ const __filename = fileURLToPath(import.meta.url);
+ const __dirname = path.dirname(__filename);
+
+ let migrationsFolder = path.resolve(__dirname, "../../drizzle");
+ if (!fs.existsSync(migrationsFolder)) {
+ migrationsFolder = path.resolve(__dirname, "../drizzle");
+ }
+
+ await migrate(db, { migrationsFolder });
+}
diff --git a/src/db/schema.ts b/src/db/schema.ts
new file mode 100644
index 0000000..c24a39c
--- /dev/null
+++ b/src/db/schema.ts
@@ -0,0 +1,402 @@
+import {
+ pgTable,
+ varchar,
+ text,
+ jsonb,
+ boolean,
+ timestamp,
+ primaryKey,
+ unique,
+ uuid,
+ pgEnum,
+ time,
+ check,
+ index,
+ integer,
+} from "drizzle-orm/pg-core";
+import { sql } from "drizzle-orm";
+import { createInsertSchema, createSelectSchema } from "drizzle-zod";
+
+export const channelEnum = pgEnum("channel", ["email", "sms", "push", "webhook", "in-app"]);
+
+export const projects = pgTable("projects", {
+ id: uuid("id").primaryKey().defaultRandom(),
+ name: varchar("name").notNull(),
+ rateLimitRpm: integer("rate_limit_rpm"),
+ throttleLimit: integer("throttle_limit"),
+ throttleWindowHours: integer("throttle_window_hours"),
+ createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
+});
+
+export const users = pgTable(
+ "users",
+ {
+ id: uuid("id").primaryKey().defaultRandom(),
+ projectId: uuid("project_id").notNull(),
+ externalId: text("external_id").notNull(),
+ attributes: jsonb("attributes").notNull().default({}),
+ createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
+ updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(),
+ },
+ (table) => ({
+ unq: unique().on(table.projectId, table.externalId),
+ }),
+);
+
+export const apiKeyRoleEnum = pgEnum("api_key_role", ["admin", "read_only"]);
+
+export const projectApiKeys = pgTable("project_api_keys", {
+ id: uuid("id").primaryKey().defaultRandom(),
+ projectId: uuid("project_id")
+ .notNull()
+ .references(() => projects.id, { onDelete: "cascade" }),
+ keyHash: varchar("key_hash").notNull().unique(),
+ role: apiKeyRoleEnum("role").notNull().default("admin"),
+ createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
+});
+
+export const userSegments = pgTable(
+ "user_segments",
+ {
+ id: uuid("id").primaryKey().defaultRandom(),
+ userId: uuid("user_id")
+ .notNull()
+ .references(() => users.id, { onDelete: "cascade" }),
+ segment: varchar("segment").notNull(),
+ },
+ (table) => ({
+ unq: unique().on(table.userId, table.segment),
+ segmentIdx: index("segment_idx").on(table.segment),
+ }),
+);
+
+export const userContacts = pgTable(
+ "user_contacts",
+ {
+ id: uuid("id").primaryKey().defaultRandom(),
+ userId: uuid("user_id")
+ .notNull()
+ .references(() => users.id, { onDelete: "cascade" }),
+ channel: channelEnum("channel").notNull(),
+ target: text("target").notNull(),
+ label: text("label"),
+ isPrimary: boolean("is_primary").notNull().default(false),
+ enabled: boolean("enabled").notNull().default(true),
+ createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
+ },
+ (table) => ({
+ unq: unique().on(table.userId, table.channel, table.target),
+ }),
+);
+
+export const userChannelPreferences = pgTable(
+ "user_channel_preferences",
+ {
+ userId: uuid("user_id")
+ .notNull()
+ .references(() => users.id, { onDelete: "cascade" }),
+ channel: channelEnum("channel").notNull(),
+ enabled: boolean("enabled").notNull(),
+ },
+ (table) => ({
+ pk: primaryKey({ columns: [table.userId, table.channel] }),
+ }),
+);
+
+export const userTopicPreferences = pgTable(
+ "user_topic_preferences",
+ {
+ id: uuid("id").primaryKey().defaultRandom(),
+ userId: uuid("user_id")
+ .notNull()
+ .references(() => users.id, { onDelete: "cascade" }),
+ topic: varchar("topic").notNull(),
+ enabled: boolean("enabled").notNull(),
+ },
+ (table) => ({
+ unq: unique().on(table.userId, table.topic),
+ }),
+);
+
+export const contactTopicPreferences = pgTable(
+ "contact_topic_preferences",
+ {
+ id: uuid("id").primaryKey().defaultRandom(),
+ contactId: uuid("contact_id")
+ .notNull()
+ .references(() => userContacts.id, { onDelete: "cascade" }),
+ topic: varchar("topic").notNull(),
+ enabled: boolean("enabled").notNull(),
+ },
+ (table) => ({
+ unq: unique().on(table.contactId, table.topic),
+ }),
+);
+
+export const quietHours = pgTable(
+ "quiet_hours",
+ {
+ id: uuid("id").primaryKey().defaultRandom(),
+ userId: uuid("user_id").references(() => users.id, { onDelete: "cascade" }),
+ contactId: uuid("contact_id").references(() => userContacts.id, { onDelete: "cascade" }),
+ startTime: time("start_time").notNull(),
+ endTime: time("end_time").notNull(),
+ },
+ (table) => ({
+ checkOwner: check("check_owner", sql`num_nonnulls(user_id, contact_id) = 1`),
+ userIdIdx: index("quiet_hours_user_idx").on(table.userId),
+ }),
+);
+
+export const templates = pgTable(
+ "templates",
+ {
+ projectId: uuid("project_id")
+ .notNull()
+ .references(() => projects.id, { onDelete: "cascade" }),
+ id: varchar("id").notNull(),
+ channel: channelEnum("channel").notNull(),
+ topics: text("topics").array().notNull(),
+ content: jsonb("content").notNull(),
+ aiPrompts: jsonb("ai_prompts"),
+ updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(),
+ },
+ (table) => ({
+ pk: primaryKey({ columns: [table.projectId, table.id] }),
+ }),
+);
+
+export const messageLogs = pgTable(
+ "message_logs",
+ {
+ id: uuid("id").primaryKey().defaultRandom(),
+ projectId: uuid("project_id").notNull(),
+ taskId: varchar("task_id").notNull(),
+ providerMessageId: varchar("provider_message_id"),
+ templateId: varchar("template_id"),
+ workflowInstanceId: uuid("workflow_instance_id"),
+ channel: channelEnum("channel").notNull(),
+ // Delivery attempts are numbered from 1; provider engagement events are not
+ // attempts and use 0.
+ attempt: integer("attempt").default(1).notNull(),
+ /**
+ * Discriminates a delivery attempt ("attempt") from a provider engagement
+ * event ("opened", "clicked", "bounced", โฆ). Without it an engagement row
+ * collides with the delivery row for the same (task, channel, attempt).
+ */
+ kind: varchar("kind").notNull().default("attempt"),
+ status: varchar("status").notNull(),
+ /**
+ * Groups every message produced by one `notify()` call. Null for sends that
+ * did not name a campaign, which is every send made before this column
+ * existed โ treat null as "unattributed", not as a campaign of its own.
+ */
+ campaignId: varchar("campaign_id"),
+ /**
+ * Provider-specific detail that would otherwise be discarded: the clicked
+ * URL on a click event, the bounce subtype on a bounce. Deliberately loose
+ * โ every provider reports these differently.
+ */
+ metadata: jsonb("metadata"),
+ timestamp: timestamp("timestamp", { withTimezone: true }).defaultNow().notNull(),
+ },
+ (table) => ({
+ projectIdx: index("message_logs_project_idx").on(table.projectId),
+ taskIdx: index("task_idx").on(table.taskId),
+ projectIdTaskIdIdx: index("message_logs_project_task_idx").on(table.projectId, table.taskId),
+ providerMsgIdx: index("provider_msg_idx").on(table.providerMessageId),
+ projectTimeIdx: index("msg_log_proj_time_idx").on(table.projectId, table.timestamp),
+ templateIdx: index("msg_log_template_idx").on(table.projectId, table.templateId),
+ workflowIdx: index("msg_log_workflow_idx").on(table.projectId, table.workflowInstanceId),
+ campaignIdx: index("msg_log_campaign_idx").on(table.projectId, table.campaignId),
+ taskChannelAttemptUidx: unique("task_channel_attempt_uidx").on(
+ table.taskId,
+ table.channel,
+ table.attempt,
+ table.kind,
+ ),
+ }),
+);
+
+/**
+ * Addresses that must not be contacted again on a given channel.
+ *
+ * Rows are written from provider webhooks (an unsubscribe, a spam complaint, a
+ * hard bounce) and by hand through the API. The engine consults this table
+ * before dispatching, so a suppression is a hard stop rather than a preference
+ * โ `priority: "critical"` does not override it. Removing a row is the only way
+ * back, and that is deliberately a manual act.
+ */
+export const suppressions = pgTable(
+ "suppressions",
+ {
+ id: uuid("id").primaryKey().defaultRandom(),
+ projectId: uuid("project_id").notNull(),
+ channel: channelEnum("channel").notNull(),
+ /** The address itself, normalised: email is lower-cased, others stored verbatim. */
+ target: varchar("target").notNull(),
+ /** `unsubscribed` | `complained` | `bounced` | `manual`. */
+ reason: varchar("reason").notNull(),
+ /** Where it came from โ a provider name, or `api` for a manual entry. */
+ source: varchar("source"),
+ /** The delivery that triggered it, when a provider webhook is the origin. */
+ taskId: varchar("task_id"),
+ createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
+ },
+ (table) => ({
+ // One row per address per channel; a second complaint must not error.
+ projectChannelTargetUidx: unique("suppression_project_channel_target_uidx").on(
+ table.projectId,
+ table.channel,
+ table.target,
+ ),
+ projectIdx: index("suppression_project_idx").on(table.projectId),
+ // The engine's hot path: "is this address suppressed on this channel?"
+ lookupIdx: index("suppression_lookup_idx").on(table.projectId, table.channel, table.target),
+ }),
+);
+
+export const workflowStatusEnum = pgEnum("workflow_status", [
+ "pending",
+ "running",
+ "completed",
+ "failed",
+]);
+
+export const workflowDefinitions = pgTable(
+ "workflow_definitions",
+ {
+ id: uuid("id").primaryKey().defaultRandom(),
+ projectId: uuid("project_id").notNull(),
+ name: varchar("name").notNull(),
+ steps: jsonb("steps").notNull(),
+ createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
+ },
+ (table) => ({
+ nameUnq: unique().on(table.projectId, table.name),
+ }),
+);
+
+export const workflowInstances = pgTable(
+ "workflow_instances",
+ {
+ id: uuid("id").primaryKey().defaultRandom(),
+ projectId: uuid("project_id").notNull(),
+ name: varchar("name").notNull(),
+ status: workflowStatusEnum("status").notNull().default("pending"),
+ input: jsonb("input").default({}),
+ createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
+ updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(),
+ },
+ (table) => ({
+ nameIdx: index("workflow_name_idx").on(table.name),
+ }),
+);
+
+export const workflowSteps = pgTable(
+ "workflow_steps",
+ {
+ id: uuid("id").primaryKey().defaultRandom(),
+ projectId: uuid("project_id").notNull(),
+ instanceId: uuid("instance_id")
+ .notNull()
+ .references(() => workflowInstances.id, { onDelete: "cascade" }),
+ stepIndex: varchar("step_index").notNull(), // We can use string format for index (e.g. "0", "1", "0.1") or a deterministic id
+ action: varchar("action").notNull(), // e.g. "notify", "wait", "run"
+ output: jsonb("output"),
+ error: text("error"),
+ createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
+ },
+ (table) => ({
+ unq: unique().on(table.instanceId, table.stepIndex),
+ }),
+);
+
+export const workflowWaiters = pgTable(
+ "workflow_waiters",
+ {
+ id: uuid("id").primaryKey().defaultRandom(),
+ projectId: uuid("project_id").notNull(),
+ instanceId: uuid("instance_id")
+ .notNull()
+ .references(() => workflowInstances.id, { onDelete: "cascade" }),
+ eventName: varchar("event_name").notNull(),
+ matchCriteria: jsonb("match_criteria").notNull(), // { userId: "123" }
+ expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
+ createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
+ },
+ (table) => ({
+ eventIdx: index("waiter_event_idx").on(table.eventName),
+ instanceIdx: index("waiter_instance_idx").on(table.instanceId),
+ compositeWaitIdx: index("waiter_comp_idx").on(
+ table.eventName,
+ table.projectId,
+ table.expiresAt,
+ ),
+ matchCriteriaIdx: index("waiter_match_idx").using("gin", table.matchCriteria),
+ }),
+);
+
+export const deliveryOutbox = pgTable(
+ "delivery_outbox",
+ {
+ taskId: varchar("task_id").notNull(),
+ channel: channelEnum("channel").notNull(),
+ destination: text("destination").notNull(),
+ providerMessageId: varchar("provider_message_id"),
+ createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
+ },
+ (table) => ({
+ pk: primaryKey({ columns: [table.taskId, table.channel, table.destination] }),
+ }),
+);
+
+export const scheduledPayloads = pgTable("scheduled_payloads", {
+ taskId: varchar("task_id").primaryKey(),
+ payload: jsonb("payload").notNull(),
+ createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
+});
+
+// โโโ Generated Zod Schemas โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+export const insertProjectSchema = createInsertSchema(projects);
+export const selectProjectSchema = createSelectSchema(projects);
+
+export const insertProjectApiKeySchema = createInsertSchema(projectApiKeys);
+export const selectProjectApiKeySchema = createSelectSchema(projectApiKeys);
+
+export const insertUserSchema = createInsertSchema(users);
+export const selectUserSchema = createSelectSchema(users);
+
+export const insertUserSegmentSchema = createInsertSchema(userSegments);
+export const selectUserSegmentSchema = createSelectSchema(userSegments);
+
+export const insertUserContactSchema = createInsertSchema(userContacts);
+export const selectUserContactSchema = createSelectSchema(userContacts);
+
+export const insertUserChannelPreferenceSchema = createInsertSchema(userChannelPreferences);
+export const selectUserChannelPreferenceSchema = createSelectSchema(userChannelPreferences);
+
+export const insertUserTopicPreferenceSchema = createInsertSchema(userTopicPreferences);
+export const selectUserTopicPreferenceSchema = createSelectSchema(userTopicPreferences);
+
+export const insertMessageLogSchema = createInsertSchema(messageLogs);
+export const selectMessageLogSchema = createSelectSchema(messageLogs);
+
+export const insertSuppressionSchema = createInsertSchema(suppressions);
+export const selectSuppressionSchema = createSelectSchema(suppressions);
+
+export const insertWorkflowInstanceSchema = createInsertSchema(workflowInstances);
+export const selectWorkflowInstanceSchema = createSelectSchema(workflowInstances);
+
+export const insertWorkflowStepSchema = createInsertSchema(workflowSteps);
+export const selectWorkflowStepSchema = createSelectSchema(workflowSteps);
+
+export const insertWorkflowWaiterSchema = createInsertSchema(workflowWaiters);
+export const selectWorkflowWaiterSchema = createSelectSchema(workflowWaiters);
+
+export const insertDeliveryOutboxSchema = createInsertSchema(deliveryOutbox);
+export const selectDeliveryOutboxSchema = createSelectSchema(deliveryOutbox);
+
+export const insertScheduledPayloadSchema = createInsertSchema(scheduledPayloads);
+export const selectScheduledPayloadSchema = createSelectSchema(scheduledPayloads);
diff --git a/src/idempotency/index.ts b/src/idempotency/index.ts
new file mode 100644
index 0000000..d9c201e
--- /dev/null
+++ b/src/idempotency/index.ts
@@ -0,0 +1,50 @@
+import type { Redis } from "@/index.js";
+
+export interface IdempotencyOptions {
+ redis: Redis;
+ keyPrefix: string;
+ ttlSeconds?: number;
+}
+
+/**
+ * SETNX-based idempotency guard.
+ * Returns true from checkAndMark() only the first time a given ID is seen
+ * within the TTL window; subsequent calls return false (duplicate / retry).
+ */
+export class IdempotencyGuard {
+ private readonly redis: Redis;
+ private readonly keyPrefix: string;
+ private readonly ttlSeconds: number;
+
+ constructor({ redis, keyPrefix, ttlSeconds = 86_400 }: IdempotencyOptions) {
+ this.redis = redis;
+ this.keyPrefix = keyPrefix;
+ this.ttlSeconds = ttlSeconds;
+ }
+
+ private key(id: string): string {
+ return `${this.keyPrefix}:${id}`;
+ }
+
+ /** Atomically mark id as processed. Returns true on first call; false if already seen. */
+ async checkAndMark(id: string, customTtlSeconds?: number): Promise {
+ const ttl = customTtlSeconds ?? this.ttlSeconds;
+ const result = await this.redis.set(this.key(id), "1", "EX", ttl, "NX");
+ return result === "OK";
+ }
+
+ /** Unconditionally mark id as processed (e.g. to upgrade a short-lived lock). */
+ async markProcessed(id: string, customTtlSeconds?: number): Promise {
+ const ttl = customTtlSeconds ?? this.ttlSeconds;
+ await this.redis.set(this.key(id), "1", "EX", ttl);
+ }
+
+ async isProcessed(id: string): Promise {
+ return (await this.redis.get(this.key(id))) !== null;
+ }
+
+ /** Remove the idempotency marker (useful in tests or manual rollbacks). */
+ async unmark(id: string): Promise {
+ await this.redis.del(this.key(id));
+ }
+}
diff --git a/src/index.ts b/src/index.ts
new file mode 100644
index 0000000..862563e
--- /dev/null
+++ b/src/index.ts
@@ -0,0 +1,19 @@
+export * from "./config/index.js";
+export * from "./contracts/index.js";
+export * from "./db/index.js";
+export * from "./idempotency/index.js";
+export * from "./logger/index.js";
+export * from "./metrics/index.js";
+export * from "./queue/index.js";
+export * from "./rate-limiter/index.js";
+export * from "./redis/index.js";
+export * from "./repositories/index.js";
+
+export * from "./shared/index.js";
+export * from "./templates/index.js";
+export * from "./transport/index.js";
+export * from "./unsubscribe/index.js";
+export * from "./workers/index.js";
+export * from "./workflows/index.js";
+export * from "./client.js";
+export * from "./server.js";
diff --git a/src/logger/index.ts b/src/logger/index.ts
new file mode 100644
index 0000000..e60fdba
--- /dev/null
+++ b/src/logger/index.ts
@@ -0,0 +1,60 @@
+import pino, { type Logger as PinoLogger, type LoggerOptions as PinoOptions } from "pino";
+
+export type LogLevel = "fatal" | "error" | "warn" | "info" | "debug" | "trace" | "silent";
+
+export type Logger = PinoLogger;
+
+export interface LoggerOptions {
+ name: string;
+ level?: LogLevel;
+ pretty?: boolean;
+ context?: Record;
+}
+
+export function createLogger({ name, level = "info", pretty, context }: LoggerOptions): Logger {
+ const usePretty = pretty ?? process.env["NODE_ENV"] !== "production";
+
+ const options: PinoOptions = {
+ name,
+ level,
+ formatters: {
+ level(label) {
+ return { level: label };
+ },
+ },
+ timestamp: pino.stdTimeFunctions.isoTime,
+ serializers: {
+ err: pino.stdSerializers.err,
+ error: pino.stdSerializers.err,
+ req: pino.stdSerializers.req,
+ res: pino.stdSerializers.res,
+ },
+ base: { service: name, ...context },
+ };
+
+ if (usePretty) {
+ options.transport = {
+ target: "pino-pretty",
+ options: {
+ colorize: true,
+ translateTime: "SYS:standard",
+ ignore: "pid,hostname",
+ messageFormat: "{service} | {msg}",
+ },
+ };
+ }
+
+ return pino(options);
+}
+
+export function withRequestId(logger: Logger, requestId: string): Logger {
+ return logger.child({ requestId });
+}
+
+export function withContext(logger: Logger, context: Record): Logger {
+ return logger.child(context);
+}
+
+export function childLogger(logger: Logger, bindings: Record): Logger {
+ return logger.child(bindings);
+}
diff --git a/src/metrics/index.ts b/src/metrics/index.ts
new file mode 100644
index 0000000..152024d
--- /dev/null
+++ b/src/metrics/index.ts
@@ -0,0 +1,53 @@
+import promClient from "prom-client";
+
+const register = new promClient.Registry();
+promClient.collectDefaultMetrics({ register });
+
+export const metrics = {
+ messagesPublished: new promClient.Counter({
+ name: "notifkit_messages_published_total",
+ help: "Total messages published to inbound streams",
+ labelNames: ["channel", "priority"],
+ registers: [register],
+ }),
+ messagesProcessed: new promClient.Counter({
+ name: "notifkit_messages_processed_total",
+ help: "Total messages processed by workers",
+ labelNames: ["worker", "status"],
+ registers: [register],
+ }),
+ deliverySuccess: new promClient.Counter({
+ name: "notifkit_delivery_success_total",
+ help: "Total successful deliveries",
+ labelNames: ["channel"],
+ registers: [register],
+ }),
+ deliveryFailed: new promClient.Counter({
+ name: "notifkit_delivery_failed_total",
+ help: "Total failed deliveries",
+ labelNames: ["channel", "reason"],
+ registers: [register],
+ }),
+ workerActiveTasks: new promClient.Gauge({
+ name: "notifkit_worker_active_tasks",
+ help: "Number of currently active tasks per worker",
+ labelNames: ["worker"],
+ registers: [register],
+ }),
+ queueSize: new promClient.Gauge({
+ name: "notifkit_queue_size",
+ help: "Current size of streams",
+ labelNames: ["stream"],
+ registers: [register],
+ }),
+ pendingAcks: new promClient.Gauge({
+ name: "notifkit_pending_acks",
+ help: "Number of pending un-acked messages per group",
+ labelNames: ["group"],
+ registers: [register],
+ }),
+};
+
+export function getMetricsRegistry() {
+ return register;
+}
diff --git a/src/queue/index.ts b/src/queue/index.ts
new file mode 100644
index 0000000..52f56f2
--- /dev/null
+++ b/src/queue/index.ts
@@ -0,0 +1,501 @@
+import type { Redis } from "@/index.js";
+import type { Logger } from "@/index.js";
+import {
+ StreamEventSchema,
+ type StreamEvent,
+ type StreamName,
+ type ConsumerGroup,
+ readBaseConfig,
+ STREAMS,
+} from "@/index.js";
+import { metrics } from "@/metrics/index.js";
+
+// โโโ Types โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+export interface StreamMessage {
+ id: string;
+ event: StreamEvent;
+ /** Stream this message was read from. Required to ack/claim against the right one. */
+ stream?: string;
+}
+
+export interface PendingEntry {
+ id: string;
+ consumer: string;
+ idleMs: number;
+ deliveryCount: number;
+ /** Stream this entry is pending on. Stream ids are not unique across streams. */
+ stream: StreamName;
+}
+
+// โโโ Internal helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+function parseMessage(id: string, fields: string[] | null, logger?: Logger): StreamMessage | null {
+ if (!fields) return null;
+
+ const dataIndex = fields.indexOf("data");
+ if (dataIndex === -1) return null;
+
+ const raw = fields[dataIndex + 1];
+ if (!raw) return null;
+
+ let decoded: unknown;
+ try {
+ decoded = JSON.parse(raw);
+ } catch (err) {
+ logger?.warn({ id, err }, "failed to parse stream event JSON");
+ return null;
+ }
+
+ const parsed = StreamEventSchema.safeParse(decoded);
+ if (!parsed.success) {
+ logger?.warn({ id, error: parsed.error.issues }, "failed to parse stream event");
+ return null;
+ }
+
+ return { id, event: parsed.data };
+}
+
+// โโโ StreamProducer โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+export interface StreamProducerOptions {
+ redis: Redis;
+ stream: StreamName;
+ logger?: Logger;
+ maxLen?: number;
+}
+
+export class StreamProducer {
+ private readonly redis: Redis;
+ private readonly stream: StreamName;
+ private readonly logger?: Logger;
+ private readonly maxLen: number;
+
+ constructor({ redis, stream, logger, maxLen }: StreamProducerOptions) {
+ this.redis = redis;
+ this.stream = stream;
+ this.logger = logger;
+ this.maxLen = maxLen ?? readBaseConfig().QUEUE_MAX_LEN;
+ }
+
+ async publish(partial: Omit): Promise {
+ const event: StreamEvent = {
+ ...partial,
+ id: crypto.randomUUID(),
+ timestamp: new Date().toISOString(),
+ };
+
+ const messageId = await this.redis.xadd(
+ this.stream,
+ "MAXLEN",
+ "~",
+ String(this.maxLen),
+ "*",
+ "data",
+ JSON.stringify(event),
+ );
+
+ if (!messageId) throw new Error(`XADD to ${this.stream} returned null`);
+
+ this.logger?.debug(
+ { stream: this.stream, messageId, eventType: event.type, eventId: event.id },
+ "event published",
+ );
+
+ return messageId;
+ }
+
+ private async monitorMaxLen(stream: string) {
+ if (Math.random() < 0.05) {
+ // Check ~5% of the time to avoid overhead
+ try {
+ const len = await this.redis.xlen(stream);
+ metrics.queueSize.set({ stream }, len);
+
+ if (len > this.maxLen * 0.8) {
+ this.logger?.warn(
+ { stream, len, maxLen: this.maxLen },
+ "stream is nearing MAXLEN limit (80%+)",
+ );
+ }
+
+ const dlqLen = await this.redis.xlen(STREAMS.DEAD_LETTER);
+ metrics.queueSize.set({ stream: STREAMS.DEAD_LETTER }, dlqLen);
+ } catch (err) {
+ this.logger?.debug({ err }, "failed to monitor stream length");
+ }
+ }
+ }
+
+ async publishBatch(
+ partials: Omit[],
+ ): Promise<{ messageIds: string[]; eventIds: string[] }> {
+ if (partials.length === 0) return { messageIds: [], eventIds: [] };
+
+ const pipeline = this.redis.pipeline();
+ const timestamp = new Date().toISOString();
+
+ const eventIds: string[] = [];
+
+ for (const partial of partials) {
+ const id = crypto.randomUUID();
+ eventIds.push(id);
+ const event: StreamEvent = {
+ ...partial,
+ id,
+ timestamp,
+ };
+
+ pipeline.xadd(
+ this.stream,
+ "MAXLEN",
+ "~",
+ String(this.maxLen),
+ "*",
+ "data",
+ JSON.stringify(event),
+ );
+ }
+
+ const results = await pipeline.exec();
+ if (!results) throw new Error(`Pipeline execution failed for ${this.stream}`);
+
+ const messageIds: string[] = [];
+ for (let i = 0; i < results.length; i++) {
+ const result = results[i];
+ if (!result) throw new Error("Pipeline result is undefined");
+ const [err, msgId] = result;
+ if (err) throw err;
+ messageIds.push(msgId as string);
+ }
+
+ this.logger?.debug({ stream: this.stream, count: partials.length }, "batch events published");
+
+ this.monitorMaxLen(this.stream).catch(() => {});
+
+ return { messageIds, eventIds };
+ }
+}
+
+// โโโ StreamConsumer โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+export interface StreamConsumerOptions {
+ redis: Redis;
+ stream: StreamName | StreamName[];
+ group: ConsumerGroup;
+ consumer: string;
+ dlqStream?: StreamName;
+ logger?: Logger;
+ batchSize?: number;
+ blockMs?: number;
+}
+
+type XReadGroupResult = Array<[string, Array<[string, string[] | null]>]> | null;
+
+export class StreamConsumer {
+ readonly redis: Redis;
+ private readonly blockingRedis: Redis;
+ private readonly streams: StreamName[];
+ private readonly group: ConsumerGroup;
+ private readonly consumer: string;
+ private readonly dlqStream?: StreamName;
+ private readonly logger?: Logger;
+ private readonly batchSize: number;
+ private readonly blockMs: number;
+ private running = false;
+
+ constructor({
+ redis,
+ stream,
+ group,
+ consumer,
+ dlqStream,
+ logger,
+ batchSize = 10,
+ blockMs = 5_000,
+ }: StreamConsumerOptions) {
+ this.redis = redis;
+ this.blockingRedis = redis.duplicate();
+ this.streams = Array.isArray(stream) ? stream : [stream];
+ this.group = group;
+ this.consumer = consumer;
+ this.dlqStream = dlqStream;
+ this.logger = logger;
+ this.batchSize = batchSize;
+ this.blockMs = blockMs;
+ }
+
+ async ensureGroup(): Promise {
+ for (const s of this.streams) {
+ try {
+ // Start from the beginning so events published before the first worker
+ // comes online are not silently skipped. Retention is controlled by the
+ // producer's MAXLEN policy rather than consumer-group creation time.
+ await this.redis.xgroup("CREATE", s, this.group, "0", "MKSTREAM");
+ this.logger?.info({ stream: s, group: this.group }, "consumer group created");
+ } catch (err) {
+ if (err instanceof Error && err.message.includes("BUSYGROUP")) {
+ this.logger?.debug({ stream: s, group: this.group }, "consumer group already exists");
+ continue;
+ }
+ throw err;
+ }
+ }
+ }
+
+ async *readBatch(): AsyncGenerator {
+ this.running = true;
+ let retryDelay = 1000;
+
+ while (this.running) {
+ try {
+ let currentStreams = [...this.streams];
+ // Weighted fair queuing: 10% of the time, rotate the priority order
+ // to prevent starvation of low priority queues.
+ if (currentStreams.length > 1 && Math.random() < 0.1) {
+ const offset = Math.floor(Math.random() * (currentStreams.length - 1)) + 1;
+ for (let i = 0; i < offset; i++) {
+ currentStreams.push(currentStreams.shift()!);
+ }
+ }
+
+ let results;
+ let deadConnectionTimer: ReturnType | undefined;
+ try {
+ results = (await Promise.race([
+ this.blockingRedis.xreadgroup(
+ "GROUP",
+ this.group,
+ this.consumer,
+ "COUNT",
+ String(this.batchSize),
+ "BLOCK",
+ String(this.blockMs),
+ "STREAMS",
+ ...currentStreams,
+ ...currentStreams.map(() => ">"),
+ ),
+ new Promise((_, reject) => {
+ deadConnectionTimer = setTimeout(
+ () => reject(new Error("XREADGROUP_TIMEOUT_DEAD_CONNECTION")),
+ this.blockMs + 5000,
+ );
+ }),
+ ])) as XReadGroupResult;
+ } catch (err: any) {
+ if (err.message === "XREADGROUP_TIMEOUT_DEAD_CONNECTION") {
+ this.logger?.warn(
+ "XREADGROUP took too long, assuming dead connection. Disconnecting...",
+ );
+ this.blockingRedis.disconnect();
+ throw err;
+ }
+ throw err;
+ } finally {
+ // Whichever side of the race loses stays pending, so the guard timer
+ // outlives the read it was guarding. Normally the loop turns over once
+ // per `blockMs` and only a couple accumulate โ but whenever the read
+ // returns straight away, one timer per iteration piles up unbounded.
+ clearTimeout(deadConnectionTimer);
+ }
+
+ retryDelay = 1000; // reset on success
+
+ if (!results) continue;
+
+ const batch: StreamMessage[] = [];
+ for (const [streamName, messages] of results) {
+ for (const [id, fields] of messages) {
+ const msg = parseMessage(id, fields, this.logger);
+ if (!msg) {
+ await this.redis.xack(streamName, this.group, id);
+ continue;
+ }
+ // Attach original stream name for dynamic acking
+ msg.stream = streamName as StreamName;
+ batch.push(msg);
+ }
+ }
+ if (batch.length > 0) yield batch;
+ } catch (err) {
+ if (
+ !this.running &&
+ err instanceof Error &&
+ err?.message?.toLowerCase?.().includes("connection is closed")
+ ) {
+ break; // Expected during graceful shutdown
+ }
+ this.logger?.error({ err }, "error reading from stream");
+ await new Promise((resolve) => setTimeout(resolve, retryDelay));
+ retryDelay = Math.min(retryDelay * 2, 30_000); // Exponential backoff up to 30s
+ }
+ }
+ }
+
+ async ack(messageId: string | string[], stream?: string): Promise {
+ const s = stream ?? this.streams[0]!;
+ const ids = Array.isArray(messageId) ? messageId : [messageId];
+ if (ids.length === 0) return;
+ await this.redis.xack(s, this.group, ...ids);
+ this.logger?.debug({ stream: s, count: ids.length }, "messages acknowledged");
+ }
+
+ async nack(messageId: string, event: StreamEvent, stream?: string): Promise {
+ const s = stream ?? this.streams[0]!;
+ if (this.dlqStream) {
+ // Sequential rather than pipelined, and in this order: the ack is what
+ // makes the drop final, so it must never run against a DLQ write that
+ // did not land. A pipeline is not a transaction โ both commands execute
+ // regardless โ so inspecting its results afterwards would be too late.
+ // Throwing here leaves the message pending for the recovery loop, which
+ // is the recoverable end of the trade.
+ const dlqId = await this.redis.xadd(
+ this.dlqStream,
+ "*",
+ "data",
+ JSON.stringify({
+ ...event,
+ dlq: { originalStream: s, ackedAt: new Date().toISOString() },
+ }),
+ );
+
+ if (!dlqId) {
+ throw new Error(`XADD to dead-letter stream ${this.dlqStream} returned null`);
+ }
+
+ await this.redis.xack(s, this.group, messageId);
+ this.logger?.warn(
+ { stream: s, dlqStream: this.dlqStream, messageId, eventId: event.id, dlqId },
+ "message moved to dead-letter queue and acked",
+ );
+ } else {
+ await this.ack(messageId, s);
+ }
+ }
+
+ async stop(): Promise {
+ this.running = false;
+ try {
+ await this.blockingRedis.quit();
+ } catch (err: any) {
+ if (!err?.message?.toLowerCase?.().includes("connection is closed")) {
+ throw err;
+ }
+ }
+ }
+}
+
+// โโโ PendingMessageScanner โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+export interface PendingMessageScannerOptions {
+ redis: Redis;
+ stream: StreamName | StreamName[];
+ group: ConsumerGroup;
+ consumer: string;
+ logger?: Logger;
+}
+
+export class PendingMessageScanner {
+ private readonly redis: Redis;
+ private readonly streams: StreamName[];
+ private readonly group: ConsumerGroup;
+ private readonly consumer: string;
+ private readonly logger?: Logger;
+
+ constructor({ redis, stream, group, consumer, logger }: PendingMessageScannerOptions) {
+ this.redis = redis;
+ this.streams = Array.isArray(stream) ? stream : [stream];
+ this.group = group;
+ this.consumer = consumer;
+ this.logger = logger;
+ }
+
+ async getPendingCount(): Promise {
+ let total = 0;
+ for (const s of this.streams) {
+ const summary = await this.redis.xpending(s, this.group);
+ if (Array.isArray(summary) && summary.length > 0) {
+ const count = summary[0];
+ if (typeof count === "number") total += count;
+ }
+ }
+ return total;
+ }
+
+ /** Pending entries for one stream, or across all of them when `stream` is omitted. */
+ async getPendingEntries(limit = 100, stream?: StreamName): Promise {
+ const streams = stream ? [stream] : this.streams;
+ const allEntries: PendingEntry[] = [];
+
+ for (const s of streams) {
+ const result = await this.redis.xpending(s, this.group, "-", "+", limit);
+ if (Array.isArray(result)) {
+ for (const item of result) {
+ if (Array.isArray(item)) {
+ allEntries.push({
+ id: item[0],
+ consumer: item[1],
+ idleMs: item[2],
+ deliveryCount: item[3],
+ stream: s,
+ });
+ }
+ }
+ }
+ if (allEntries.length >= limit) break;
+ }
+ return allEntries.slice(0, limit);
+ }
+
+ async autoclaim(minIdleMs: number, limit = 10): Promise {
+ const recovered: StreamMessage[] = [];
+
+ for (const s of this.streams) {
+ if (recovered.length >= limit) break;
+
+ // Scope the scan to THIS stream. Message ids are `-` and are not
+ // unique across streams, so claiming an id gathered from another stream
+ // can silently claim an unrelated message.
+ const pending = await this.getPendingEntries(limit * 2, s);
+ const toClaim = pending
+ .filter((p) => p.idleMs > minIdleMs * Math.pow(2, p.deliveryCount - 1))
+ .slice(0, limit - recovered.length);
+
+ if (toClaim.length === 0) continue;
+
+ const ids = toClaim.map((p) => p.id);
+ // Let Redis arbitrate rather than claiming unconditionally. Two scanners
+ // routinely list the same entry, and with a min-idle of 0 both claims
+ // succeed and the message is processed twice; with the threshold applied
+ // server-side the loser sees an entry whose idle time the winner has just
+ // reset, and gets nothing back. The filter above is stricter than this,
+ // so nothing it selected is excluded here for being too fresh.
+ const result = (await this.redis.xclaim(
+ s,
+ this.group,
+ this.consumer,
+ minIdleMs,
+ ...ids,
+ )) as Array<[string, string[] | null]>;
+
+ for (const raw of result) {
+ if (!raw) continue;
+ const [id, fields] = raw;
+ const msg = parseMessage(id, fields, this.logger);
+ if (msg) {
+ msg.stream = s;
+ recovered.push(msg);
+ }
+ }
+ }
+
+ if (recovered.length > 0) {
+ this.logger?.info(
+ { group: this.group, count: recovered.length },
+ "autoclaimed pending messages",
+ );
+ }
+
+ return recovered;
+ }
+}
diff --git a/src/rate-limiter/index.ts b/src/rate-limiter/index.ts
new file mode 100644
index 0000000..12ab7c2
--- /dev/null
+++ b/src/rate-limiter/index.ts
@@ -0,0 +1,210 @@
+import type { Redis } from "@/index.js";
+import { LRUCache } from "@/shared/index.js";
+
+import { randomUUID } from "crypto";
+
+export interface ThrottleResult {
+ allowed: boolean;
+ count: number;
+ limit: number;
+}
+
+// โโโ Per-project overrides โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+/**
+ * Throttle overrides stored on the project row. `null` on either field means
+ * "no override" โ fall back to the process-wide default.
+ */
+export interface ProjectThrottleSettings {
+ throttleLimit: number | null;
+ throttleWindowHours: number | null;
+}
+
+const NO_OVERRIDES: ProjectThrottleSettings = {
+ throttleLimit: null,
+ throttleWindowHours: null,
+};
+
+export interface ProjectSettingsCacheOptions {
+ maxSize?: number;
+ ttlMs?: number;
+}
+
+/**
+ * Caches per-project throttle overrides for the engine.
+ *
+ * The throttle check runs once per notification, so an uncached lookup here
+ * would put a Postgres round trip on the hot path. Projects with no overrides
+ * are cached as well โ the common case must not cost a query per message.
+ *
+ * The TTL bounds staleness on its own; `invalidate()` exists so a settings
+ * change published over pub/sub applies immediately rather than at expiry.
+ */
+export class ProjectSettingsCache {
+ private readonly cache: LRUCache;
+ /**
+ * Lookups already on the wire, keyed by project.
+ *
+ * The cache only fills once a query has come back, so without this a cold
+ * project at the start of a campaign puts one query per in-flight message on
+ * Postgres before the first answer lands โ exactly when the database is
+ * busiest. Followers wait on the leader's promise instead.
+ */
+ private readonly inFlight = new Map>();
+
+ constructor(
+ private readonly load: (projectId: string) => Promise,
+ { maxSize = 1000, ttlMs = 60_000 }: ProjectSettingsCacheOptions = {},
+ ) {
+ this.cache = new LRUCache(maxSize, ttlMs);
+ }
+
+ /**
+ * Throws whatever the loader throws. The caller decides whether a settings
+ * lookup failure should drop the message or fall back to defaults.
+ */
+ async get(projectId: string): Promise {
+ const cached = this.cache.get(projectId);
+ if (cached) return cached;
+
+ const existing = this.inFlight.get(projectId);
+ if (existing) return existing;
+
+ // Assigned before any callback below can run, since `load` cannot settle
+ // within this synchronous block.
+ let pending!: Promise;
+ pending = this.load(projectId)
+ .then((settings) => {
+ const resolved = settings ?? NO_OVERRIDES;
+ // Only cache while this is still the current lookup: an invalidate()
+ // that landed while the query was on the wire means the answer in hand
+ // already describes the old settings.
+ if (this.inFlight.get(projectId) === pending) {
+ this.cache.set(projectId, resolved);
+ }
+ return resolved;
+ })
+ .finally(() => {
+ // Cleared on failure too, so one bad lookup does not pin every later
+ // caller to the same rejection.
+ if (this.inFlight.get(projectId) === pending) {
+ this.inFlight.delete(projectId);
+ }
+ });
+
+ this.inFlight.set(projectId, pending);
+ return pending;
+ }
+
+ invalidate(projectId: string): void {
+ this.cache.delete(projectId);
+ // A lookup that started before the change would write a stale value on
+ // arrival; dropping it here sends the next caller back to the database.
+ this.inFlight.delete(projectId);
+ }
+
+ clear(): void {
+ this.cache.clear();
+ this.inFlight.clear();
+ }
+}
+
+// โโโ UserThrottle โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+// True sliding window counter using Redis ZSET: max N sends per window per user.
+
+export interface UserThrottleOptions {
+ redis: Redis;
+ maxPerHour?: number;
+ windowHours?: number;
+}
+
+export interface ThrottleCheckOptions {
+ /** Per-project cap for this window. `0` blocks every non-critical send. */
+ limit?: number | null;
+ /** Per-project window length in hours. */
+ windowHours?: number | null;
+ /** Future send time. The window is evaluated at that instant, not at now. */
+ scheduledAt?: string;
+}
+
+/** Reject stored values that would make the window meaningless. */
+function positiveOrNull(value: number | null | undefined): number | null {
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : null;
+}
+
+/** A limit of 0 is a legitimate kill switch, so zero is allowed here. */
+function nonNegativeOrNull(value: number | null | undefined): number | null {
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
+}
+
+export class UserThrottle {
+ private readonly redis: Redis;
+ private readonly maxPerHour: number;
+ private readonly windowHours: number;
+
+ constructor({ redis, maxPerHour = 3, windowHours = 1 }: UserThrottleOptions) {
+ this.redis = redis;
+ this.maxPerHour = maxPerHour;
+ this.windowHours = windowHours;
+ }
+
+ /**
+ * @param projectId Tenant that owns `userId`. User ids are caller-supplied
+ * external ids, so they collide across tenants and MUST be namespaced โ
+ * otherwise one tenant's traffic throttles another's.
+ * @param options Per-project overrides. Values that are absent, null, or
+ * nonsensical fall back to this instance's defaults.
+ */
+ async check(
+ projectId: string,
+ userId: string,
+ priority?: string,
+ options: ThrottleCheckOptions = {},
+ ): Promise {
+ const limit = nonNegativeOrNull(options.limit) ?? this.maxPerHour;
+ const windowHours = positiveOrNull(options.windowHours) ?? this.windowHours;
+
+ if (priority === "critical") {
+ return { allowed: true, count: 0, limit };
+ }
+
+ const windowMs = windowHours * 3600_000;
+ const key = `throttle:${projectId}:user:${userId}`;
+ const targetTime = options.scheduledAt ? new Date(options.scheduledAt).getTime() : Date.now();
+ const windowStart = targetTime - windowMs;
+ const memberId = randomUUID();
+
+ const LUA_THROTTLE = `
+ redis.call("ZREMRANGEBYSCORE", KEYS[1], "-inf", ARGV[1])
+ local count = redis.call("ZCARD", KEYS[1])
+ if tonumber(count) < tonumber(ARGV[2]) then
+ redis.call("ZADD", KEYS[1], tonumber(ARGV[3]), ARGV[4])
+ redis.call("EXPIRE", KEYS[1], tonumber(ARGV[5]))
+ return tonumber(count) + 1
+ end
+ return tonumber(count) + 1
+ `;
+
+ // The key must outlive the window it is counting. For a future-dated send
+ // that means surviving until targetTime plus one more window, so a task
+ // scheduled for next week still counts against the right bucket.
+ const windowSeconds = Math.ceil(windowMs / 1000);
+ const ttlSeconds = Math.max(
+ windowSeconds,
+ Math.ceil((targetTime - Date.now()) / 1000) + windowSeconds,
+ );
+
+ const count = (await this.redis.eval(
+ LUA_THROTTLE,
+ 1,
+ key,
+ windowStart,
+ limit,
+ targetTime,
+ memberId,
+ ttlSeconds,
+ )) as number;
+
+ return { allowed: count <= limit, count, limit };
+ }
+}
diff --git a/src/redis/index.ts b/src/redis/index.ts
new file mode 100644
index 0000000..61aaf60
--- /dev/null
+++ b/src/redis/index.ts
@@ -0,0 +1,78 @@
+import { Redis, type RedisOptions } from "ioredis";
+import type { Logger } from "@/index.js";
+
+export interface RedisClientOptions {
+ url: string;
+ name?: string;
+ logger?: Logger;
+ redisOptions?: Partial;
+}
+
+export class RedisClient {
+ readonly native: Redis;
+
+ private readonly logger?: Logger;
+ private isClosing = false;
+
+ constructor({ url, name = "notifkit", logger, redisOptions }: RedisClientOptions) {
+ this.logger = logger;
+
+ this.native = new Redis(url, {
+ maxRetriesPerRequest: null,
+ enableReadyCheck: true,
+ lazyConnect: false,
+ connectionName: name,
+ ...redisOptions,
+ });
+
+ this.native.on("connect", () => {
+ this.logger?.info({ url: redactUrl(url) }, "redis connected");
+ });
+
+ this.native.on("ready", () => {
+ this.logger?.debug("redis ready");
+ });
+
+ this.native.on("error", (err: Error) => {
+ this.logger?.error({ err }, "redis client error");
+ });
+
+ this.native.on("close", () => {
+ if (!this.isClosing) {
+ this.logger?.warn("redis connection closed unexpectedly");
+ }
+ });
+
+ this.native.on("reconnecting", () => {
+ this.logger?.warn("redis reconnecting");
+ });
+ }
+
+ async healthCheck(): Promise {
+ try {
+ const pong = await this.native.ping();
+ return pong === "PONG";
+ } catch {
+ return false;
+ }
+ }
+
+ async disconnect(): Promise {
+ this.isClosing = true;
+ this.logger?.info("disconnecting redis");
+ await this.native.quit();
+ this.logger?.info("redis disconnected");
+ }
+}
+
+function redactUrl(url: string): string {
+ try {
+ const parsed = new URL(url);
+ if (parsed.password) parsed.password = "***";
+ return parsed.toString();
+ } catch {
+ return "[invalid-url]";
+ }
+}
+
+export { Redis, type RedisOptions };
diff --git a/src/repositories/index.ts b/src/repositories/index.ts
new file mode 100644
index 0000000..b2e7ec9
--- /dev/null
+++ b/src/repositories/index.ts
@@ -0,0 +1,1220 @@
+import { eq, and, sql as drizzleSql, inArray, desc } from "drizzle-orm";
+import type { Db } from "@/index.js";
+import type { Preferences, ContactChannel } from "@/contracts/index.js";
+import {
+ users,
+ userSegments,
+ userContacts,
+ userChannelPreferences,
+ userTopicPreferences,
+ contactTopicPreferences,
+ quietHours,
+ templates,
+ projects,
+ workflowInstances,
+ workflowSteps,
+ workflowWaiters,
+ workflowDefinitions,
+ projectApiKeys,
+ suppressions,
+ messageLogs,
+} from "@/db/schema.js";
+
+// โโโ Domain types โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+export interface UserProfile {
+ userId: string; // Maps to externalId
+ language?: string;
+ timezone?: string;
+ email?: string | null;
+}
+
+export interface UserRecord extends UserProfile {
+ segments: string[];
+ preferences: Preferences;
+}
+
+export interface UserContact {
+ id: string; // uuid
+ userId: string; // externalId
+ channel: ContactChannel;
+ target: string;
+ preferences: Preferences;
+ active: boolean;
+}
+
+export interface TemplateRecord {
+ id: string;
+ channel: string;
+ topics: string[];
+ content: unknown;
+ /** Template-level AI prompts, merged with per-request ones by the engine. */
+ aiPrompts?: Record | null;
+}
+
+export type DevicePlatform = "fcm" | "apns" | "web";
+
+export interface DeviceToken {
+ id: string;
+ userId: string;
+ deviceToken: string;
+ platform: DevicePlatform;
+ active: boolean;
+}
+
+export interface NotificationPreference {
+ userId: string;
+ eventType: string;
+ optedIn: boolean;
+}
+
+// โโโ UserRepository โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+export class UserRepository {
+ constructor(private readonly db: Db) {}
+
+ async findById(projectId: string, userId: string): Promise {
+ const rows = await this.db
+ .select()
+ .from(users)
+ .where(and(eq(users.externalId, userId), eq(users.projectId, projectId)))
+ .limit(1);
+
+ if (!rows[0]) return null;
+ const attrs = rows[0].attributes as any;
+ return {
+ userId: rows[0].externalId,
+ language: attrs.language,
+ timezone: attrs.timezone,
+ email: attrs.email,
+ };
+ }
+
+ async findRecordById(projectId: string, userId: string): Promise {
+ const userRows = await this.db
+ .select()
+ .from(users)
+ .where(and(eq(users.externalId, userId), eq(users.projectId, projectId)));
+ if (!userRows[0]) return null;
+
+ const userRow = userRows[0];
+ const internalId = userRow.id;
+ const attrs = userRow.attributes as any;
+
+ const [segmentRows, topicRows, channelRows, qhRows] = await Promise.all([
+ this.db.select().from(userSegments).where(eq(userSegments.userId, internalId)),
+ this.db
+ .select()
+ .from(userTopicPreferences)
+ .where(eq(userTopicPreferences.userId, internalId)),
+ this.db
+ .select()
+ .from(userChannelPreferences)
+ .where(eq(userChannelPreferences.userId, internalId)),
+ this.db.select().from(quietHours).where(eq(quietHours.userId, internalId)),
+ ]);
+
+ const segments = segmentRows.map((r) => r.segment);
+
+ const topics: Record = {};
+ for (const r of topicRows) {
+ topics[r.topic] = r.enabled;
+ }
+
+ const channels: Record = {};
+ for (const r of channelRows) {
+ channels[r.channel] = r.enabled;
+ }
+
+ const quietHoursList = qhRows.map((r) => ({
+ start: r.startTime.slice(0, 5),
+ end: r.endTime.slice(0, 5),
+ }));
+
+ return {
+ userId: userRow.externalId,
+ language: attrs.language,
+ timezone: attrs.timezone,
+ email: attrs.email,
+ segments,
+ preferences: {
+ channels,
+ topics,
+ quietHours: quietHoursList.length > 0 ? quietHoursList : undefined,
+ },
+ };
+ }
+
+ async findRecordsByIds(projectId: string, userIds: string[]): Promise {
+ if (userIds.length === 0) return [];
+
+ const usersRows = await this.db
+ .select()
+ .from(users)
+ .where(and(inArray(users.externalId, userIds), eq(users.projectId, projectId)));
+
+ if (usersRows.length === 0) return [];
+
+ const internalIds = usersRows.map((r) => r.id);
+
+ const [segmentRows, topicRows, channelRows, qhRows] = await Promise.all([
+ this.db.select().from(userSegments).where(inArray(userSegments.userId, internalIds)),
+ this.db
+ .select()
+ .from(userTopicPreferences)
+ .where(inArray(userTopicPreferences.userId, internalIds)),
+ this.db
+ .select()
+ .from(userChannelPreferences)
+ .where(inArray(userChannelPreferences.userId, internalIds)),
+ this.db.select().from(quietHours).where(inArray(quietHours.userId, internalIds)),
+ ]);
+
+ const segmentsByUserId = new Map();
+ for (const r of segmentRows) {
+ if (!segmentsByUserId.has(r.userId)) segmentsByUserId.set(r.userId, []);
+ segmentsByUserId.get(r.userId)!.push(r.segment);
+ }
+
+ const topicsByUserId = new Map>();
+ for (const r of topicRows) {
+ if (!topicsByUserId.has(r.userId)) topicsByUserId.set(r.userId, {});
+ topicsByUserId.get(r.userId)![r.topic] = r.enabled;
+ }
+
+ const channelsByUserId = new Map>();
+ for (const r of channelRows) {
+ if (!channelsByUserId.has(r.userId)) channelsByUserId.set(r.userId, {});
+ channelsByUserId.get(r.userId)![r.channel] = r.enabled;
+ }
+
+ const qhByUserId = new Map();
+ for (const r of qhRows) {
+ if (r.userId == null) continue;
+ if (!qhByUserId.has(r.userId)) qhByUserId.set(r.userId, []);
+ qhByUserId
+ .get(r.userId)!
+ .push({ start: r.startTime.slice(0, 5), end: r.endTime.slice(0, 5) });
+ }
+
+ const userRecords: UserRecord[] = [];
+
+ for (const userRow of usersRows) {
+ const attrs = userRow.attributes as any;
+ const internalId = userRow.id;
+
+ userRecords.push({
+ userId: userRow.externalId,
+ language: attrs.language,
+ timezone: attrs.timezone,
+ email: attrs.email,
+ segments: segmentsByUserId.get(internalId) || [],
+ preferences: {
+ channels: channelsByUserId.get(internalId) || {},
+ topics: topicsByUserId.get(internalId) || {},
+ quietHours: qhByUserId.get(internalId),
+ },
+ });
+ }
+
+ return userRecords;
+ }
+
+ async upsertFull(
+ projectId: string,
+ user: {
+ userId: string;
+ language?: string;
+ timezone?: string;
+ email?: string | null;
+ segments: string[];
+ preferences: Preferences;
+ },
+ ): Promise {
+ await this.db.transaction(async (tx) => {
+ await tx
+ .insert(users)
+ .values({
+ projectId,
+ externalId: user.userId,
+ attributes: {
+ language: user.language,
+ timezone: user.timezone,
+ email: user.email,
+ },
+ })
+ .onConflictDoUpdate({
+ target: [users.projectId, users.externalId],
+ set: {
+ attributes: {
+ language: user.language,
+ timezone: user.timezone,
+ email: user.email,
+ },
+ updatedAt: new Date(),
+ },
+ });
+
+ const internalIdRows = await tx
+ .select({ id: users.id })
+ .from(users)
+ .where(and(eq(users.externalId, user.userId), eq(users.projectId, projectId)));
+ const internalId = internalIdRows[0]?.id;
+ if (!internalId) return;
+
+ if (user.segments && user.segments.length > 0) {
+ await tx
+ .insert(userSegments)
+ .values(user.segments.map((s) => ({ userId: internalId, segment: s })))
+ .onConflictDoNothing();
+ }
+
+ if (user.preferences && user.preferences.topics) {
+ const topicInserts = Object.entries(user.preferences.topics).map(([topic, enabled]) => ({
+ userId: internalId,
+ topic,
+ enabled,
+ }));
+ if (topicInserts.length > 0) {
+ await tx
+ .insert(userTopicPreferences)
+ .values(topicInserts)
+ .onConflictDoUpdate({
+ target: [userTopicPreferences.userId, userTopicPreferences.topic],
+ set: { enabled: drizzleSql`excluded.enabled` },
+ });
+ }
+ }
+
+ if (user.preferences?.channels) {
+ const channelInserts = Object.entries(user.preferences.channels).map(
+ ([channel, enabled]) => ({
+ userId: internalId,
+ channel: channel as ContactChannel,
+ enabled,
+ }),
+ );
+ if (channelInserts.length > 0) {
+ await tx
+ .insert(userChannelPreferences)
+ .values(channelInserts)
+ .onConflictDoUpdate({
+ target: [userChannelPreferences.userId, userChannelPreferences.channel],
+ set: { enabled: drizzleSql`excluded.enabled` },
+ });
+ }
+ }
+
+ if (user.preferences?.quietHours !== undefined) {
+ await tx.delete(quietHours).where(eq(quietHours.userId, internalId));
+ if (user.preferences.quietHours.length > 0) {
+ await tx.insert(quietHours).values(
+ user.preferences.quietHours.map((window) => ({
+ userId: internalId,
+ startTime: window.start,
+ endTime: window.end,
+ })),
+ );
+ }
+ }
+ });
+ }
+
+ async upsertManyFull(
+ projectId: string,
+ usersList: Array<{
+ userId: string;
+ language?: string;
+ timezone?: string;
+ email?: string | null;
+ segments: string[];
+ preferences: Preferences;
+ }>,
+ ): Promise {
+ if (usersList.length === 0) return;
+
+ let attempts = 0;
+ while (attempts < 3) {
+ try {
+ await this.db.transaction(async (tx) => {
+ await tx
+ .insert(users)
+ .values(
+ usersList.map((u) => ({
+ projectId,
+ externalId: u.userId,
+ attributes: {
+ language: u.language,
+ timezone: u.timezone,
+ email: u.email,
+ },
+ })),
+ )
+ .onConflictDoUpdate({
+ target: [users.projectId, users.externalId],
+ set: {
+ attributes: drizzleSql`excluded.attributes`,
+ updatedAt: new Date(),
+ },
+ });
+
+ const internalIdRows = await tx
+ .select({ id: users.id, externalId: users.externalId })
+ .from(users)
+ .where(
+ and(
+ inArray(
+ users.externalId,
+ usersList.map((u) => u.userId),
+ ),
+ eq(users.projectId, projectId),
+ ),
+ );
+
+ const idMap = new Map(internalIdRows.map((r) => [r.externalId, r.id]));
+
+ const segmentInserts: any[] = [];
+ const topicInserts: any[] = [];
+ const channelInserts: any[] = [];
+ const quietHoursInserts: any[] = [];
+
+ for (const u of usersList) {
+ const internalId = idMap.get(u.userId);
+ if (!internalId) continue;
+
+ if (u.segments && u.segments.length > 0) {
+ for (const s of u.segments) {
+ segmentInserts.push({ userId: internalId, segment: s });
+ }
+ }
+
+ if (u.preferences?.topics) {
+ for (const [topic, enabled] of Object.entries(u.preferences.topics)) {
+ topicInserts.push({ userId: internalId, topic, enabled });
+ }
+ }
+
+ if (u.preferences?.channels) {
+ for (const [channel, enabled] of Object.entries(u.preferences.channels)) {
+ channelInserts.push({
+ userId: internalId,
+ channel: channel as ContactChannel,
+ enabled,
+ });
+ }
+ }
+
+ if (u.preferences?.quietHours && u.preferences.quietHours.length > 0) {
+ for (const window of u.preferences.quietHours) {
+ quietHoursInserts.push({
+ userId: internalId,
+ startTime: window.start,
+ endTime: window.end,
+ });
+ }
+ }
+ }
+
+ const segmentSet = new Set();
+ const dedupedSegmentInserts: any[] = [];
+ for (const s of segmentInserts) {
+ const key = `${s.userId}:${s.segment}`;
+ if (!segmentSet.has(key)) {
+ segmentSet.add(key);
+ dedupedSegmentInserts.push(s);
+ }
+ }
+
+ const topicMap = new Map();
+ for (const t of topicInserts) {
+ topicMap.set(`${t.userId}:${t.topic}`, t);
+ }
+ const dedupedTopicInserts = Array.from(topicMap.values());
+
+ const channelMap = new Map();
+ for (const c of channelInserts) {
+ channelMap.set(`${c.userId}:${c.channel}`, c);
+ }
+ const dedupedChannelInserts = Array.from(channelMap.values());
+
+ if (dedupedSegmentInserts.length > 0) {
+ await tx.insert(userSegments).values(dedupedSegmentInserts).onConflictDoNothing();
+ }
+
+ if (dedupedTopicInserts.length > 0) {
+ await tx
+ .insert(userTopicPreferences)
+ .values(dedupedTopicInserts)
+ .onConflictDoUpdate({
+ target: [userTopicPreferences.userId, userTopicPreferences.topic],
+ set: { enabled: drizzleSql`excluded.enabled` },
+ });
+ }
+
+ if (dedupedChannelInserts.length > 0) {
+ await tx
+ .insert(userChannelPreferences)
+ .values(dedupedChannelInserts)
+ .onConflictDoUpdate({
+ target: [userChannelPreferences.userId, userChannelPreferences.channel],
+ set: { enabled: drizzleSql`excluded.enabled` },
+ });
+ }
+
+ const usersWithQuietHours = usersList.filter(
+ (u) => u.preferences?.quietHours !== undefined,
+ );
+ const internalIdsToClearQuietHours = usersWithQuietHours
+ .map((u) => idMap.get(u.userId))
+ .filter(Boolean) as string[];
+
+ if (internalIdsToClearQuietHours.length > 0) {
+ await tx
+ .delete(quietHours)
+ .where(inArray(quietHours.userId, internalIdsToClearQuietHours));
+ }
+ if (quietHoursInserts.length > 0) {
+ await tx.insert(quietHours).values(quietHoursInserts);
+ }
+ });
+ return;
+ } catch (err: any) {
+ attempts++;
+ if (attempts >= 3 || (err.code !== "40001" && err.code !== "40P01")) {
+ throw err;
+ }
+ await new Promise((resolve) => setTimeout(resolve, Math.pow(2, attempts) * 100));
+ }
+ }
+ }
+
+ async updatePartial(
+ projectId: string,
+ userId: string,
+ patch: {
+ language?: string;
+ timezone?: string;
+ email?: string | null;
+ segments?: string[];
+ preferences?: Preferences;
+ },
+ ): Promise {
+ const existing = await this.findById(projectId, userId);
+ if (!existing) return false;
+
+ const attrs = {
+ language: patch.language ?? existing.language,
+ timezone: patch.timezone ?? existing.timezone,
+ email: patch.email ?? existing.email,
+ };
+
+ await this.db.transaction(async (tx) => {
+ await tx
+ .update(users)
+ .set({ attributes: attrs, updatedAt: new Date() })
+ .where(and(eq(users.externalId, userId), eq(users.projectId, projectId)));
+
+ const internalIdRows = await tx
+ .select({ id: users.id })
+ .from(users)
+ .where(and(eq(users.externalId, userId), eq(users.projectId, projectId)));
+ const internalId = internalIdRows[0]?.id;
+ if (!internalId) return;
+
+ if (patch.segments) {
+ await tx.delete(userSegments).where(eq(userSegments.userId, internalId));
+ if (patch.segments.length > 0) {
+ await tx
+ .insert(userSegments)
+ .values(patch.segments.map((s) => ({ userId: internalId, segment: s })))
+ .onConflictDoNothing();
+ }
+ }
+
+ if (patch.preferences && patch.preferences.topics) {
+ const topicInserts = Object.entries(patch.preferences.topics).map(([topic, enabled]) => ({
+ userId: internalId,
+ topic,
+ enabled,
+ }));
+ if (topicInserts.length > 0) {
+ await tx
+ .insert(userTopicPreferences)
+ .values(topicInserts)
+ .onConflictDoUpdate({
+ target: [userTopicPreferences.userId, userTopicPreferences.topic],
+ set: { enabled: drizzleSql`excluded.enabled` },
+ });
+ }
+ }
+
+ if (patch.preferences?.channels) {
+ const channelInserts = Object.entries(patch.preferences.channels).map(
+ ([channel, enabled]) => ({
+ userId: internalId,
+ channel: channel as ContactChannel,
+ enabled,
+ }),
+ );
+ if (channelInserts.length > 0) {
+ await tx
+ .insert(userChannelPreferences)
+ .values(channelInserts)
+ .onConflictDoUpdate({
+ target: [userChannelPreferences.userId, userChannelPreferences.channel],
+ set: { enabled: drizzleSql`excluded.enabled` },
+ });
+ }
+ }
+
+ if (patch.preferences?.quietHours !== undefined) {
+ await tx.delete(quietHours).where(eq(quietHours.userId, internalId));
+ if (patch.preferences.quietHours.length > 0) {
+ await tx.insert(quietHours).values(
+ patch.preferences.quietHours.map((window) => ({
+ userId: internalId,
+ startTime: window.start,
+ endTime: window.end,
+ })),
+ );
+ }
+ }
+ });
+
+ return true;
+ }
+
+ async delete(projectId: string, userId: string): Promise {
+ const result = await this.db
+ .delete(users)
+ .where(and(eq(users.externalId, userId), eq(users.projectId, projectId)))
+ .returning();
+ return result.length > 0;
+ }
+
+ async list(
+ projectId: string,
+ limit: number,
+ cursor?: string,
+ ): Promise<{ users: UserProfile[]; nextCursor: string | null }> {
+ let query = this.db
+ .select()
+ .from(users)
+ .where(eq(users.projectId, projectId))
+ .orderBy(desc(users.createdAt))
+ .limit(limit);
+
+ if (cursor) {
+ // Decode cursor (timestamp)
+ const cursorDate = new Date(parseInt(cursor, 10));
+ query = this.db
+ .select()
+ .from(users)
+ .where(
+ and(
+ eq(users.projectId, projectId),
+ drizzleSql`${users.createdAt} < ${cursorDate.toISOString()}`,
+ ),
+ )
+ .orderBy(desc(users.createdAt))
+ .limit(limit) as any;
+ }
+
+ const rows = await query;
+ const items = rows.map((r) => {
+ const attrs = r.attributes as any;
+ return {
+ userId: r.externalId,
+ language: attrs.language,
+ timezone: attrs.timezone,
+ email: attrs.email,
+ createdAt: r.createdAt.getTime(),
+ };
+ });
+
+ const nextCursor =
+ items.length === limit ? items[items.length - 1]!.createdAt.toString() : null;
+ return { users: items, nextCursor };
+ }
+
+ async findUsersBySegment(projectId: string, segmentName: string): Promise {
+ const rows = await this.db
+ .select({ externalId: users.externalId })
+ .from(users)
+ .innerJoin(userSegments, eq(users.id, userSegments.userId))
+ .where(and(eq(userSegments.segment, segmentName), eq(users.projectId, projectId)));
+
+ return rows.map((r) => r.externalId);
+ }
+
+ async findUsersByTopic(projectId: string, topicName: string): Promise {
+ const rows = await this.db
+ .select({ externalId: users.externalId })
+ .from(users)
+ .innerJoin(userTopicPreferences, eq(users.id, userTopicPreferences.userId))
+ .where(
+ and(
+ eq(userTopicPreferences.topic, topicName),
+ eq(userTopicPreferences.enabled, true),
+ eq(users.projectId, projectId),
+ ),
+ );
+
+ return rows.map((r) => r.externalId);
+ }
+}
+
+// โโโ PreferenceRepository โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+export class PreferenceRepository {
+ constructor(private readonly db: Db) {}
+
+ async isOptedIn(projectId: string, userId: string, eventType: string): Promise {
+ const prefs = await this.findByUserId(projectId, userId);
+ const pref = prefs.find((p) => p.eventType === eventType);
+ return pref ? pref.optedIn : true;
+ }
+
+ async findByUserId(projectId: string, userId: string): Promise {
+ const internalUserIdRows = await this.db
+ .select({ id: users.id })
+ .from(users)
+ .where(and(eq(users.externalId, userId), eq(users.projectId, projectId)));
+ const internalId = internalUserIdRows[0]?.id;
+ if (!internalId) return [];
+
+ const rows = await this.db
+ .select()
+ .from(userTopicPreferences)
+ .where(eq(userTopicPreferences.userId, internalId));
+
+ return rows.map((r) => ({
+ userId,
+ eventType: r.topic,
+ optedIn: r.enabled,
+ }));
+ }
+}
+
+// โโโ ContactRepository โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+export class ContactRepository {
+ constructor(private readonly db: Db) {}
+
+ async findByUserId(projectId: string, userId: string): Promise {
+ const internalUserIdRows = await this.db
+ .select({ id: users.id })
+ .from(users)
+ .where(and(eq(users.externalId, userId), eq(users.projectId, projectId)));
+ const internalId = internalUserIdRows[0]?.id;
+ if (!internalId) return [];
+
+ const rows = await this.db
+ .select()
+ .from(userContacts)
+ .where(eq(userContacts.userId, internalId));
+ if (rows.length === 0) return [];
+
+ const topicRows = await this.db
+ .select()
+ .from(contactTopicPreferences)
+ .where(
+ inArray(
+ contactTopicPreferences.contactId,
+ rows.map((r) => r.id),
+ ),
+ );
+
+ const topicsByContact = new Map>();
+ for (const t of topicRows) {
+ if (!topicsByContact.has(t.contactId)) topicsByContact.set(t.contactId, {});
+ topicsByContact.get(t.contactId)![t.topic] = t.enabled;
+ }
+
+ return rows.map((r) => ({
+ id: r.id,
+ userId,
+ channel: r.channel as ContactChannel,
+ target: r.target,
+ preferences: { topics: topicsByContact.get(r.id) ?? {} },
+ active: r.enabled,
+ }));
+ }
+
+ /** Resolve active addressable contacts for a batch without an N+1 query. */
+ async findActiveByUserIds(
+ projectId: string,
+ userIds: string[],
+ ): Promise |