feat(api): integrate openapi-fetch for Hetzner and Hostinger services, enhancing API interaction and simplifying data fetching

This commit is contained in:
Mauricio Siu
2025-07-23 01:21:11 -06:00
parent 6758ef1542
commit f3dc480b70
10 changed files with 36799 additions and 258 deletions

View File

@@ -71,11 +71,11 @@ export const ShowHostingerServers = () => {
?.sort((a, b) => {
// Sort by monthly promotional price (first_period_price)
const monthlyPriceA =
a.prices.find(
a.prices?.find(
(p) => p.period === 1 && p.period_unit === "month",
)?.first_period_price || 0;
const monthlyPriceB =
b.prices.find(
b.prices?.find(
(p) => p.period === 1 && p.period_unit === "month",
)?.first_period_price || 0;
return monthlyPriceA - monthlyPriceB;
@@ -108,19 +108,19 @@ export const ShowHostingerServers = () => {
<div className="grid grid-cols-2 gap-2 text-sm">
<div className="flex items-center gap-1">
<Cpu className="h-4 w-4 text-blue-500" />
<span>{plan.metadata.cpus} vCPU</span>
<span>{plan.metadata?.cpus} vCPU</span>
</div>
<div className="flex items-center gap-1">
<MemoryStick className="h-4 w-4 text-green-500" />
<span>{plan.metadata.memory}GB RAM</span>
<span>{plan.metadata?.memory}GB RAM</span>
</div>
<div className="flex items-center gap-1">
<HardDrive className="h-4 w-4 text-orange-500" />
<span>{plan.metadata.disk_space}GB NVMe</span>
<span>{plan.metadata?.disk_space}GB NVMe</span>
</div>
<div className="flex items-center gap-1">
<Globe className="h-4 w-4 text-indigo-500" />
<span>{plan.metadata.bandwidth}TB</span>
<span>{plan.metadata?.bandwidth}TB</span>
</div>
</div>
@@ -148,7 +148,7 @@ export const ShowHostingerServers = () => {
{/* Monthly prices */}
<TabsContent value="monthly" className="mt-2">
{plan.prices
{plan.prices?.
.filter(
(p) =>
p.period === 1 && p.period_unit === "month",
@@ -187,20 +187,20 @@ export const ShowHostingerServers = () => {
{/* Yearly prices */}
<TabsContent value="yearly" className="mt-2">
{plan.prices
{plan.prices?.
.filter(
(p) =>
p.period === 1 && p.period_unit === "year",
)
.map((price) => {
const monthlyEquivalent = plan.prices.find(
const monthlyEquivalent = plan.prices?.find(
(p) =>
p.period === 1 &&
p.period_unit === "month",
);
const savings = monthlyEquivalent
? calculateSavings(
monthlyEquivalent.price / 100,
monthlyEquivalent?.price / 100,
price.first_period_price / 100,
)
: 0;
@@ -247,20 +247,20 @@ export const ShowHostingerServers = () => {
{/* Biennial prices */}
<TabsContent value="biennial" className="mt-2">
{plan.prices
{plan.prices?.
.filter(
(p) =>
p.period === 2 && p.period_unit === "year",
)
.map((price) => {
const monthlyEquivalent = plan.prices.find(
const monthlyEquivalent = plan.prices?.find(
(p) =>
p.period === 1 &&
p.period_unit === "month",
);
const savings = monthlyEquivalent
? calculateSavings(
(monthlyEquivalent.price / 100) * 24,
(monthlyEquivalent?.price / 100) * 24,
price.first_period_price / 100,
)
: 0;

View File

@@ -182,7 +182,8 @@
"tsx": "^4.16.2",
"typescript": "^5.8.3",
"vite-tsconfig-paths": "4.3.2",
"vitest": "^1.6.1"
"vitest": "^1.6.1",
"openapi-typescript": "7.8.0"
},
"ct3aMetadata": {
"initVersion": "7.25.2"

View File

@@ -7,26 +7,16 @@ import { createTRPCRouter, protectedProcedure } from "../trpc";
export const hetznerRouter = createTRPCRouter({
locations: protectedProcedure.query(async () => {
const apiKey = process.env.HETZNER_API_KEY;
if (!apiKey) {
throw new Error("Hetzner API key not configured");
}
return await fetchHetznerLocations(apiKey);
const locations = await fetchHetznerLocations();
console.log(locations);
return locations;
}),
serverTypes: protectedProcedure.query(async () => {
const apiKey = process.env.HETZNER_API_KEY;
if (!apiKey) {
throw new Error("Hetzner API key not configured");
}
return await fetchHetznerServerTypes(apiKey);
return await fetchHetznerServerTypes();
}),
servers: protectedProcedure.query(async () => {
const apiKey = process.env.HETZNER_API_KEY;
if (!apiKey) {
throw new Error("Hetzner API key not configured");
}
return await fetchHetznerServers(apiKey);
return await fetchHetznerServers();
}),
});

View File

@@ -1,34 +1,16 @@
import {
fetchHostingerCatalog,
fetchHostingerDataCenters,
fetchHostingerServers,
} from "@dokploy/server/index";
import { createTRPCRouter, protectedProcedure } from "../trpc";
export const hostingerRouter = createTRPCRouter({
vpsPlans: protectedProcedure.query(async () => {
const apiKey = process.env.HOSTINGER_API_KEY;
if (!apiKey) {
throw new Error("Hostinger API key not configured");
}
const catalogItems = await fetchHostingerCatalog(apiKey);
return catalogItems.filter((item) => item.name.startsWith("KVM"));
}),
servers: protectedProcedure.query(async () => {
const apiKey = process.env.HOSTINGER_API_KEY;
if (!apiKey) {
return [];
}
return await fetchHostingerServers(apiKey);
const catalogItems = await fetchHostingerCatalog();
return catalogItems.filter((item) => item?.name?.startsWith("KVM"));
}),
dataCenters: protectedProcedure.query(async () => {
const apiKey = process.env.HOSTINGER_API_KEY;
if (!apiKey) {
throw new Error("Hostinger API key not configured");
}
return await fetchHostingerDataCenters(apiKey);
return await fetchHostingerDataCenters();
}),
});

View File

@@ -28,6 +28,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"openapi-fetch": "0.14.0",
"@ai-sdk/anthropic": "^1.2.12",
"@ai-sdk/azure": "^1.3.23",
"@ai-sdk/cohere": "^1.2.10",

View File

@@ -1,118 +1,49 @@
import createClient from "openapi-fetch";
import type { paths } from "../types/hetzner-types";
const HETZNER_API_URL = "https://api.hetzner.cloud/v1";
interface HetznerLocation {
id: number;
name: string;
description: string;
country: string;
city: string;
latitude: number;
longitude: number;
network_zone: string;
}
const hetznerApiKey = process.env.HETZNER_API_KEY;
interface HetznerServerType {
id: number;
name: string;
description: string;
cores: number;
memory: number;
disk: number;
prices: {
location: string;
price_hourly: {
net: string;
gross: string;
};
price_monthly: {
net: string;
gross: string;
};
}[];
storage_type: string;
cpu_type: string;
architecture: string;
}
const client = createClient<paths>({ baseUrl: HETZNER_API_URL });
interface HetznerServer {
id: number;
name: string;
status: string;
created: string;
server_type: {
id: number;
name: string;
description: string;
cores: number;
memory: number;
disk: number;
};
public_net: {
ipv4: {
ip: string;
};
ipv6: {
ip: string;
};
};
}
export const fetchHetznerLocations = async (
apiKey: string,
): Promise<HetznerLocation[]> => {
const response = await fetch(`${HETZNER_API_URL}/locations`, {
export const fetchHetznerLocations = async () => {
const { data, error } = await client.GET("/locations", {
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
Authorization: `Bearer ${hetznerApiKey}`,
},
});
if (!response.ok) {
throw new Error(
`Failed to fetch Hetzner locations: ${response.statusText}`,
);
if (error) {
throw new Error(`Failed to fetch Hetzner locations: ${error}`);
}
const data = (await response.json()) as { locations?: HetznerLocation[] };
return data.locations || [];
return data;
};
export const fetchHetznerServerTypes = async (
apiKey: string,
): Promise<HetznerServerType[]> => {
const response = await fetch(`${HETZNER_API_URL}/server_types`, {
export const fetchHetznerServerTypes = async () => {
const { data, error } = await client.GET("/server_types", {
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
Authorization: `Bearer ${hetznerApiKey}`,
},
});
if (!response.ok) {
throw new Error(
`Failed to fetch Hetzner server types: ${response.statusText}`,
);
if (error) {
throw new Error(`Failed to fetch Hetzner server types: ${error}`);
}
const data = (await response.json()) as {
server_types?: HetznerServerType[];
};
return data.server_types || [];
return data;
};
export const fetchHetznerServers = async (
apiKey: string,
): Promise<HetznerServer[]> => {
const response = await fetch(`${HETZNER_API_URL}/servers`, {
export const fetchHetznerServers = async () => {
const { data, error } = await client.GET("/servers", {
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
Authorization: `Bearer ${hetznerApiKey}`,
},
});
if (!response.ok) {
throw new Error(`Failed to fetch Hetzner servers: ${response.statusText}`);
if (error) {
throw new Error(`Failed to fetch Hetzner servers: ${error}`);
}
const data = (await response.json()) as { servers?: HetznerServer[] };
return data.servers || [];
return data;
};

View File

@@ -1,113 +1,35 @@
import createClient from "openapi-fetch";
import type { paths } from "../types/hostinger-types";
const HOSTINGER_API_URL = "https://developers.hostinger.com";
const hostingerApiKey = process.env.HOSTINGER_API_KEY;
interface HostingerCatalogItem {
id: string;
name: string;
category: string;
prices: Array<{
id: string;
name: string;
currency: string;
price: number; // en centavos
first_period_price: number; // precio promocional en centavos
period: number;
period_unit: string;
}>;
metadata: {
cpus: string;
memory: string;
bandwidth: string;
disk_space: string;
network: string;
};
}
const client = createClient<paths>({ baseUrl: HOSTINGER_API_URL });
interface HostingerServer {
id: string;
name: string;
status: string;
created_at: string;
ip_address: string;
plan: {
name: string;
cpu: number;
ram: number;
storage: number;
};
location: string;
}
interface HostingerDataCenter {
id: number;
name: string;
location: string;
country: string;
}
// Obtener catalog items (productos VPS con precios reales)
export const fetchHostingerCatalog = async (
apiKey: string,
): Promise<HostingerCatalogItem[]> => {
const response = await fetch(
`${HOSTINGER_API_URL}/api/billing/v1/catalog?category=VPS`,
{
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
},
);
if (!response.ok) {
throw new Error(
`Failed to fetch Hostinger catalog: ${response.statusText}`,
);
}
const data = (await response.json()) as HostingerCatalogItem[];
return data || [];
};
// Obtener VPS existentes
export const fetchHostingerServers = async (
apiKey: string,
): Promise<HostingerServer[]> => {
const response = await fetch(
`${HOSTINGER_API_URL}/api/vps/v1/virtual-machines`,
{
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
},
);
if (!response.ok) {
// Si no hay servidores o falla, retornamos array vacío
return [];
}
const data = (await response.json()) as { data?: HostingerServer[] };
return data.data || [];
};
// Obtener data centers disponibles
export async function fetchHostingerDataCenters(
apiKey: string,
): Promise<HostingerDataCenter[]> {
const response = await fetch(`${HOSTINGER_API_URL}/api/vps/v1/data-centers`, {
export const fetchHostingerCatalog = async () => {
const { data, error } = await client.GET("/api/billing/v1/catalog", {
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
Authorization: `Bearer ${hostingerApiKey}`,
},
});
if (!response.ok) {
throw new Error(
`Failed to fetch Hostinger data centers: ${response.statusText}`,
);
if (error) {
throw new Error(`Failed to fetch Hostinger catalog: ${error}`);
}
const data = (await response.json()) as { data?: HostingerDataCenter[] };
return data.data || [];
}
return data;
};
export const fetchHostingerDataCenters = async () => {
const { data, error } = await client.GET("/api/vps/v1/data-centers", {
headers: {
Authorization: `Bearer ${hostingerApiKey}`,
},
});
if (error) {
throw new Error(`Failed to fetch Hostinger data centers: ${error}`);
}
return data;
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

184
pnpm-lock.yaml generated
View File

@@ -515,6 +515,9 @@ importers:
memfs:
specifier: ^4.17.2
version: 4.17.2
openapi-typescript:
specifier: 7.8.0
version: 7.8.0(typescript@5.8.3)
tailwindcss:
specifier: ^3.4.17
version: 3.4.17
@@ -705,6 +708,9 @@ importers:
ollama-ai-provider:
specifier: ^1.2.0
version: 1.2.0(zod@3.25.32)
openapi-fetch:
specifier: 0.14.0
version: 0.14.0
otpauth:
specifier: ^9.4.0
version: 9.4.0
@@ -3091,6 +3097,16 @@ packages:
peerDependencies:
'@redis/client': ^1.0.0
'@redocly/ajv@8.11.2':
resolution: {integrity: sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==}
'@redocly/config@0.22.2':
resolution: {integrity: sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ==}
'@redocly/openapi-core@1.34.3':
resolution: {integrity: sha512-3arRdUp1fNx55itnjKiUhO6t4Mf91TsrTIYINDNLAZPS0TPd5YpiXRctwjel0qqWoOOhjA34cZ3m4dksLDFUYg==}
engines: {node: '>=18.17.0', npm: '>=9.5.0'}
'@rollup/rollup-android-arm-eabi@4.41.1':
resolution: {integrity: sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==}
cpu: [arm]
@@ -3652,6 +3668,10 @@ packages:
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
engines: {node: '>= 6.0.0'}
agent-base@7.1.4:
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
engines: {node: '>= 14'}
aggregate-error@3.1.0:
resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
engines: {node: '>=8'}
@@ -3676,6 +3696,10 @@ packages:
ansi-align@3.0.1:
resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==}
ansi-colors@4.1.3:
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
engines: {node: '>=6'}
ansi-escapes@7.0.0:
resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==}
engines: {node: '>=18'}
@@ -3908,6 +3932,9 @@ packages:
resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==}
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
change-case@5.4.4:
resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==}
character-entities-html4@2.1.0:
resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==}
@@ -4018,6 +4045,9 @@ packages:
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
engines: {node: '>=12.5.0'}
colorette@1.4.0:
resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==}
colorette@2.0.20:
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
@@ -4851,6 +4881,10 @@ packages:
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
engines: {node: '>= 6'}
https-proxy-agent@7.0.6:
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
engines: {node: '>= 14'}
human-signals@5.0.0:
resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
engines: {node: '>=16.17.0'}
@@ -4898,6 +4932,10 @@ packages:
resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==}
engines: {node: '>=12'}
index-to-position@1.1.0:
resolution: {integrity: sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg==}
engines: {node: '>=18'}
inflation@2.1.0:
resolution: {integrity: sha512-t54PPJHG1Pp7VQvxyVCJ9mBbjG3Hqryges9bXoOO6GExCPa+//i/d5GSuFtpx3ALLd7lgIAur6zrIlBQyJuMlQ==}
engines: {node: '>= 0.8.0'}
@@ -5072,6 +5110,10 @@ packages:
js-file-download@0.4.12:
resolution: {integrity: sha512-rML+NkoD08p5Dllpjo0ffy4jRHeY6Zsapvr/W86N7E0yuzAO6qa5X9+xog6zQNlH102J7IXljNY2FtS6Lj3ucg==}
js-levenshtein@1.1.6:
resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==}
engines: {node: '>=0.10.0'}
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
@@ -5520,6 +5562,10 @@ packages:
minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
minimatch@5.1.6:
resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
engines: {node: '>=10'}
minimatch@7.4.6:
resolution: {integrity: sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==}
engines: {node: '>=10'}
@@ -5774,6 +5820,9 @@ packages:
resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
engines: {node: '>=18'}
openapi-fetch@0.14.0:
resolution: {integrity: sha512-PshIdm1NgdLvb05zp8LqRQMNSKzIlPkyMxYFxwyHR+UlKD4t2nUjkDhNxeRbhRSEd3x5EUNh2w5sJYwkhOH4fg==}
openapi-path-templating@2.2.1:
resolution: {integrity: sha512-eN14VrDvl/YyGxxrkGOHkVkWEoPyhyeydOUrbvjoz8K5eIGgELASwN1eqFOJ2CTQMGCy2EntOK1KdtJ8ZMekcg==}
engines: {node: '>=12.20.0'}
@@ -5785,6 +5834,15 @@ packages:
openapi-types@12.1.3:
resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
openapi-typescript-helpers@0.0.15:
resolution: {integrity: sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw==}
openapi-typescript@7.8.0:
resolution: {integrity: sha512-1EeVWmDzi16A+siQlo/SwSGIT7HwaFAVjvMA7/jG5HMLSnrUOzPL7uSTRZZa4v/LCRxHTApHKtNY6glApEoiUQ==}
hasBin: true
peerDependencies:
typescript: ^5.x
otpauth@9.4.0:
resolution: {integrity: sha512-fHIfzIG5RqCkK9cmV8WU+dPQr9/ebR5QOwGZn2JAr1RQF+lmAuLL2YdtdqvmBjNmgJlYk3KZ4a0XokaEhg1Jsw==}
@@ -5833,6 +5891,10 @@ packages:
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
engines: {node: '>=8'}
parse-json@8.3.0:
resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==}
engines: {node: '>=18'}
parseley@0.12.1:
resolution: {integrity: sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==}
@@ -5927,6 +5989,10 @@ packages:
resolution: {integrity: sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==}
engines: {node: '>=12'}
pluralize@8.0.0:
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
engines: {node: '>=4'}
pngjs@5.0.0:
resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
engines: {node: '>=10.13.0'}
@@ -6641,6 +6707,10 @@ packages:
resolution: {integrity: sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==}
engines: {node: '>=16'}
supports-color@10.0.0:
resolution: {integrity: sha512-HRVVSbCCMbj7/kdWF9Q+bbckjBHLtHMEoJWlkmYzzdwhYMkjkOwubLM6t7NbWKjgKamGDrWL1++KrjUO1t9oAQ==}
engines: {node: '>=18'}
supports-preserve-symlinks-flag@1.0.0:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
@@ -6826,6 +6896,10 @@ packages:
resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==}
engines: {node: '>=12.20'}
type-fest@4.41.0:
resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
engines: {node: '>=16'}
type-is@1.6.18:
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
engines: {node: '>= 0.6'}
@@ -6895,6 +6969,9 @@ packages:
peerDependencies:
browserslist: '>= 4.21.0'
uri-js-replace@1.0.1:
resolution: {integrity: sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==}
url-parse@1.5.10:
resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
@@ -7120,6 +7197,9 @@ packages:
yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
yaml-ast-parser@0.0.43:
resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==}
yaml@2.8.0:
resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==}
engines: {node: '>= 14.6'}
@@ -9259,6 +9339,29 @@ snapshots:
dependencies:
'@redis/client': 1.6.0
'@redocly/ajv@8.11.2':
dependencies:
fast-deep-equal: 3.1.3
json-schema-traverse: 1.0.0
require-from-string: 2.0.2
uri-js-replace: 1.0.1
'@redocly/config@0.22.2': {}
'@redocly/openapi-core@1.34.3(supports-color@10.0.0)':
dependencies:
'@redocly/ajv': 8.11.2
'@redocly/config': 0.22.2
colorette: 1.4.0
https-proxy-agent: 7.0.6(supports-color@10.0.0)
js-levenshtein: 1.1.6
js-yaml: 4.1.0
minimatch: 5.1.6
pluralize: 8.0.0
yaml-ast-parser: 0.0.43
transitivePeerDependencies:
- supports-color
'@rollup/rollup-android-arm-eabi@4.41.1':
optional: true
@@ -10053,10 +10156,12 @@ snapshots:
agent-base@6.0.2:
dependencies:
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
transitivePeerDependencies:
- supports-color
agent-base@7.1.4: {}
aggregate-error@3.1.0:
dependencies:
clean-stack: 2.2.0
@@ -10090,6 +10195,8 @@ snapshots:
dependencies:
string-width: 4.2.3
ansi-colors@4.1.3: {}
ansi-escapes@7.0.0:
dependencies:
environment: 1.1.0
@@ -10354,6 +10461,8 @@ snapshots:
chalk@5.4.1: {}
change-case@5.4.4: {}
character-entities-html4@2.1.0: {}
character-entities-legacy@1.1.4: {}
@@ -10475,6 +10584,8 @@ snapshots:
color-string: 1.9.1
optional: true
colorette@1.4.0: {}
colorette@2.0.20: {}
combined-stream@1.0.8:
@@ -10641,9 +10752,11 @@ snapshots:
dateformat@4.6.3: {}
debug@4.4.1:
debug@4.4.1(supports-color@10.0.0):
dependencies:
ms: 2.1.3
optionalDependencies:
supports-color: 10.0.0
decamelize@1.2.0: {}
@@ -10717,7 +10830,7 @@ snapshots:
docker-modem@5.0.6:
dependencies:
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
readable-stream: 3.6.2
split-ca: 1.0.1
ssh2: 1.15.0
@@ -10854,7 +10967,7 @@ snapshots:
esbuild-register@3.6.0(esbuild@0.19.12):
dependencies:
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
esbuild: 0.19.12
transitivePeerDependencies:
- supports-color
@@ -11099,7 +11212,7 @@ snapshots:
gel@2.1.0:
dependencies:
'@petamoriken/float16': 3.9.2
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
env-paths: 3.0.0
semver: 7.7.2
shell-quote: 1.8.2
@@ -11317,7 +11430,14 @@ snapshots:
https-proxy-agent@5.0.1:
dependencies:
agent-base: 6.0.2
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
transitivePeerDependencies:
- supports-color
https-proxy-agent@7.0.6(supports-color@10.0.0):
dependencies:
agent-base: 7.1.4
debug: 4.4.1(supports-color@10.0.0)
transitivePeerDependencies:
- supports-color
@@ -11354,6 +11474,8 @@ snapshots:
indent-string@5.0.0: {}
index-to-position@1.1.0: {}
inflation@2.1.0: {}
inflight@1.0.6:
@@ -11384,7 +11506,7 @@ snapshots:
dependencies:
'@ioredis/commands': 1.2.0
cluster-key-slot: 1.1.2
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
denque: 2.1.0
lodash.defaults: 4.2.0
lodash.isarguments: 3.1.0
@@ -11499,6 +11621,8 @@ snapshots:
js-file-download@0.4.12: {}
js-levenshtein@1.1.6: {}
js-tokens@4.0.0: {}
js-tokens@9.0.1: {}
@@ -11698,7 +11822,7 @@ snapshots:
dependencies:
chalk: 5.4.1
commander: 13.1.0
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
execa: 8.0.1
lilconfig: 3.1.3
listr2: 8.3.3
@@ -12047,7 +12171,7 @@ snapshots:
micromark@4.0.2:
dependencies:
'@types/debug': 4.1.12
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
decode-named-character-reference: 1.1.0
devlop: 1.1.0
micromark-core-commonmark: 2.0.3
@@ -12095,6 +12219,10 @@ snapshots:
dependencies:
brace-expansion: 1.1.11
minimatch@5.1.6:
dependencies:
brace-expansion: 2.0.1
minimatch@7.4.6:
dependencies:
brace-expansion: 2.0.1
@@ -12338,6 +12466,10 @@ snapshots:
dependencies:
mimic-function: 5.0.1
openapi-fetch@0.14.0:
dependencies:
openapi-typescript-helpers: 0.0.15
openapi-path-templating@2.2.1:
dependencies:
apg-lite: 1.0.4
@@ -12348,6 +12480,18 @@ snapshots:
openapi-types@12.1.3: {}
openapi-typescript-helpers@0.0.15: {}
openapi-typescript@7.8.0(typescript@5.8.3):
dependencies:
'@redocly/openapi-core': 1.34.3(supports-color@10.0.0)
ansi-colors: 4.1.3
change-case: 5.4.4
parse-json: 8.3.0
supports-color: 10.0.0
typescript: 5.8.3
yargs-parser: 21.1.1
otpauth@9.4.0:
dependencies:
'@noble/hashes': 1.7.1
@@ -12408,6 +12552,12 @@ snapshots:
json-parse-even-better-errors: 2.3.1
lines-and-columns: 1.2.4
parse-json@8.3.0:
dependencies:
'@babel/code-frame': 7.27.1
index-to-position: 1.1.0
type-fest: 4.41.0
parseley@0.12.1:
dependencies:
leac: 0.6.0
@@ -12502,6 +12652,8 @@ snapshots:
dependencies:
queue-lit: 1.5.2
pluralize@8.0.0: {}
pngjs@5.0.0: {}
postcss-import@15.1.0(postcss@8.5.3):
@@ -13255,6 +13407,8 @@ snapshots:
dependencies:
copy-anything: 3.0.5
supports-color@10.0.0: {}
supports-preserve-symlinks-flag@1.0.0: {}
swagger-client@3.35.3:
@@ -13498,6 +13652,8 @@ snapshots:
type-fest@2.19.0: {}
type-fest@4.41.0: {}
type-is@1.6.18:
dependencies:
media-typer: 0.3.0
@@ -13571,6 +13727,8 @@ snapshots:
escalade: 3.2.0
picocolors: 1.1.1
uri-js-replace@1.0.1: {}
url-parse@1.5.10:
dependencies:
querystringify: 2.2.0
@@ -13635,7 +13793,7 @@ snapshots:
vite-node@1.6.1(@types/node@18.19.104):
dependencies:
cac: 6.7.14
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
pathe: 1.1.2
picocolors: 1.1.1
vite: 5.4.19(@types/node@18.19.104)
@@ -13652,7 +13810,7 @@ snapshots:
vite-tsconfig-paths@4.3.2(typescript@5.8.3)(vite@5.4.19(@types/node@18.19.104)):
dependencies:
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
globrex: 0.1.2
tsconfck: 3.1.6(typescript@5.8.3)
optionalDependencies:
@@ -13679,7 +13837,7 @@ snapshots:
'@vitest/utils': 1.6.1
acorn-walk: 8.3.4
chai: 4.5.0
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
execa: 8.0.1
local-pkg: 0.5.1
magic-string: 0.30.17
@@ -13791,6 +13949,8 @@ snapshots:
yallist@4.0.0: {}
yaml-ast-parser@0.0.43: {}
yaml@2.8.0: {}
yargs-parser@18.1.3: