Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b1274e34a | |||
| 2f66fff993 |
@@ -14,6 +14,14 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
run: npm install -g pnpm@11.18.0
|
||||
|
||||
- name: Security audit (fails on any prod or high/critical finding)
|
||||
run: |
|
||||
pnpm audit --prod
|
||||
pnpm audit --audit-level high
|
||||
|
||||
- name: Install Docker CLI
|
||||
run: |
|
||||
apt-get update -qq
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
auto-install-peers=false
|
||||
strict-peer-dependencies=false
|
||||
save-exact=true
|
||||
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
FROM node:24-alpine AS builder
|
||||
FROM node:24.18.1-alpine AS builder
|
||||
WORKDIR /app
|
||||
|
||||
RUN npm install -g pnpm@10.26.1
|
||||
RUN npm install -g pnpm@11.18.0
|
||||
|
||||
# 1. Alle Projektdateien in den Container bringen
|
||||
COPY . .
|
||||
@@ -17,10 +17,10 @@ ENV PORT=8080
|
||||
|
||||
RUN pnpm -r --if-present run build
|
||||
|
||||
FROM node:24-alpine AS runner
|
||||
FROM node:24.18.1-alpine AS runner
|
||||
WORKDIR /app
|
||||
|
||||
RUN npm install -g pnpm@10.26.1
|
||||
RUN npm install -g pnpm@11.18.0
|
||||
|
||||
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
|
||||
COPY lib/db/package.json lib/db/
|
||||
|
||||
@@ -12,30 +12,29 @@
|
||||
"dependencies": {
|
||||
"@workspace/api-zod": "workspace:*",
|
||||
"@workspace/db": "workspace:*",
|
||||
"bcryptjs": "^3.0.3",
|
||||
"connect-pg-simple": "^10.0.0",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cors": "^2.8.6",
|
||||
"bcryptjs": "3.0.3",
|
||||
"connect-pg-simple": "10.0.0",
|
||||
"cookie-parser": "1.4.7",
|
||||
"cors": "2.8.6",
|
||||
"drizzle-orm": "catalog:",
|
||||
"express": "^5.2.1",
|
||||
"express-rate-limit": "^8.6.1",
|
||||
"express-session": "^1.19.0",
|
||||
"openid-client": "^5.7.1",
|
||||
"pino": "^9.14.0",
|
||||
"pino-http": "^10.5.0",
|
||||
"express": "5.2.1",
|
||||
"express-rate-limit": "8.6.1",
|
||||
"express-session": "1.19.0",
|
||||
"openid-client": "6.8.4",
|
||||
"pino": "10.3.1",
|
||||
"pino-http": "11.0.0",
|
||||
"zod": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcryptjs": "^3.0.0",
|
||||
"@types/connect-pg-simple": "^7.0.3",
|
||||
"@types/cookie-parser": "^1.4.10",
|
||||
"@types/cors": "^2.8.19",
|
||||
"@types/express": "^5.0.6",
|
||||
"@types/express-session": "^1.19.0",
|
||||
"@types/connect-pg-simple": "7.0.3",
|
||||
"@types/cookie-parser": "1.4.10",
|
||||
"@types/cors": "2.8.19",
|
||||
"@types/express": "5.0.6",
|
||||
"@types/express-session": "1.19.0",
|
||||
"@types/node": "catalog:",
|
||||
"esbuild": "0.27.3",
|
||||
"esbuild-plugin-pino": "^2.3.3",
|
||||
"pino-pretty": "^13.1.3",
|
||||
"thread-stream": "3.1.0"
|
||||
"esbuild": "0.28.1",
|
||||
"esbuild-plugin-pino": "2.3.3",
|
||||
"pino-pretty": "13.1.3",
|
||||
"thread-stream": "4.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
import { Router, type IRouter, type Request } from "express";
|
||||
import { Issuer, generators, type Client } from "openid-client";
|
||||
import {
|
||||
discovery,
|
||||
randomPKCECodeVerifier,
|
||||
calculatePKCECodeChallenge,
|
||||
randomState,
|
||||
buildAuthorizationUrl,
|
||||
authorizationCodeGrant,
|
||||
fetchUserInfo,
|
||||
buildEndSessionUrl,
|
||||
skipSubjectCheck,
|
||||
type Configuration,
|
||||
} from "openid-client";
|
||||
import bcrypt from "bcryptjs";
|
||||
import { eq, and, inArray, isNull } from "drizzle-orm";
|
||||
import { z } from "zod";
|
||||
@@ -12,7 +23,7 @@ import { getCsrfToken } from "../middleware/csrf";
|
||||
|
||||
const router: IRouter = Router();
|
||||
|
||||
let cachedClient: Client | null = null;
|
||||
let cachedConfig: Configuration | null = null;
|
||||
|
||||
function isOidcConfigured(): boolean {
|
||||
return !!(
|
||||
@@ -39,8 +50,8 @@ function isSafeReturnTo(value: string): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
async function getClient(): Promise<Client | null> {
|
||||
if (cachedClient) return cachedClient;
|
||||
async function getClient(): Promise<Configuration | null> {
|
||||
if (cachedConfig) return cachedConfig;
|
||||
|
||||
const keycloakUrl = process.env.KEYCLOAK_URL;
|
||||
const realm = process.env.KEYCLOAK_REALM;
|
||||
@@ -52,14 +63,9 @@ async function getClient(): Promise<Client | null> {
|
||||
}
|
||||
|
||||
try {
|
||||
const issuerUrl = `${keycloakUrl}/realms/${realm}`;
|
||||
const issuer = await Issuer.discover(issuerUrl);
|
||||
cachedClient = new issuer.Client({
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
response_types: ["code"],
|
||||
});
|
||||
return cachedClient;
|
||||
const issuerUrl = new URL(`${keycloakUrl}/realms/${realm}`);
|
||||
cachedConfig = await discovery(issuerUrl, clientId, clientSecret);
|
||||
return cachedConfig;
|
||||
} catch (err) {
|
||||
logger.error({ err }, "Failed to discover Keycloak issuer");
|
||||
return null;
|
||||
@@ -176,9 +182,9 @@ router.get("/auth/login", async (req, res): Promise<void> => {
|
||||
return;
|
||||
}
|
||||
|
||||
const codeVerifier = generators.codeVerifier();
|
||||
const codeChallenge = generators.codeChallenge(codeVerifier);
|
||||
const state = generators.state();
|
||||
const codeVerifier = randomPKCECodeVerifier();
|
||||
const codeChallenge = await calculatePKCECodeChallenge(codeVerifier);
|
||||
const state = randomState();
|
||||
|
||||
req.session.codeVerifier = codeVerifier;
|
||||
req.session.oidcState = state;
|
||||
@@ -187,7 +193,7 @@ router.get("/auth/login", async (req, res): Promise<void> => {
|
||||
}
|
||||
|
||||
const redirectUri = `${getBaseUrl(req)}/api/auth/callback`;
|
||||
const url = client.authorizationUrl({
|
||||
const url = buildAuthorizationUrl(client, {
|
||||
scope: "openid email profile",
|
||||
code_challenge: codeChallenge,
|
||||
code_challenge_method: "S256",
|
||||
@@ -195,7 +201,7 @@ router.get("/auth/login", async (req, res): Promise<void> => {
|
||||
state,
|
||||
});
|
||||
|
||||
res.redirect(url);
|
||||
res.redirect(url.href);
|
||||
});
|
||||
|
||||
router.get("/auth/callback", async (req, res): Promise<void> => {
|
||||
@@ -221,13 +227,13 @@ router.get("/auth/callback", async (req, res): Promise<void> => {
|
||||
const redirectUri = `${getBaseUrl(req)}/api/auth/callback`;
|
||||
|
||||
try {
|
||||
const params = client.callbackParams(req);
|
||||
const tokenSet = await client.callback(redirectUri, params, {
|
||||
code_verifier: codeVerifier,
|
||||
state,
|
||||
});
|
||||
const tokens = await authorizationCodeGrant(
|
||||
client,
|
||||
new URL(req.originalUrl ?? "/", getBaseUrl(req)),
|
||||
{ pkceCodeVerifier: codeVerifier, expectedState: state },
|
||||
);
|
||||
|
||||
const userinfo = await client.userinfo(tokenSet.access_token!);
|
||||
const userinfo = await fetchUserInfo(client, tokens.access_token, skipSubjectCheck);
|
||||
const dbUser = await upsertUserFromOidc(userinfo);
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
@@ -259,9 +265,9 @@ router.get("/auth/logout", async (req, res): Promise<void> => {
|
||||
req.session.destroy(() => {});
|
||||
|
||||
const client = await getClient();
|
||||
if (client && client.issuer.metadata.end_session_endpoint) {
|
||||
const logoutUrl = client.endSessionUrl({ post_logout_redirect_uri: getBaseUrl(req) });
|
||||
res.redirect(logoutUrl);
|
||||
if (client && client.serverMetadata().end_session_endpoint) {
|
||||
const logoutUrl = buildEndSessionUrl(client, { post_logout_redirect_uri: getBaseUrl(req) });
|
||||
res.redirect(logoutUrl.href);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -292,7 +298,7 @@ router.get("/auth/password-redirect", async (req, res): Promise<void> => {
|
||||
res.json({ url: null });
|
||||
return;
|
||||
}
|
||||
const realm = client.issuer.metadata.issuer ?? "";
|
||||
const realm = client.serverMetadata().issuer ?? "";
|
||||
res.json({ url: `${realm}/account/password` });
|
||||
});
|
||||
|
||||
|
||||
@@ -10,34 +10,34 @@
|
||||
"typecheck": "tsc -p tsconfig.json --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hookform/resolvers": "^3.10.0",
|
||||
"@radix-ui/react-accordion": "^1.2.12",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||
"@radix-ui/react-aspect-ratio": "^1.1.8",
|
||||
"@radix-ui/react-avatar": "^1.1.11",
|
||||
"@radix-ui/react-checkbox": "^1.3.3",
|
||||
"@radix-ui/react-collapsible": "^1.1.12",
|
||||
"@radix-ui/react-context-menu": "^2.2.16",
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||
"@radix-ui/react-hover-card": "^1.1.15",
|
||||
"@radix-ui/react-label": "^2.1.8",
|
||||
"@radix-ui/react-menubar": "^1.1.16",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.14",
|
||||
"@radix-ui/react-popover": "^1.1.15",
|
||||
"@radix-ui/react-progress": "^1.1.8",
|
||||
"@radix-ui/react-radio-group": "^1.3.8",
|
||||
"@radix-ui/react-scroll-area": "^1.2.10",
|
||||
"@radix-ui/react-select": "^2.2.6",
|
||||
"@radix-ui/react-separator": "^1.1.8",
|
||||
"@radix-ui/react-slider": "^1.3.6",
|
||||
"@radix-ui/react-slot": "^1.2.4",
|
||||
"@radix-ui/react-switch": "^1.2.6",
|
||||
"@radix-ui/react-tabs": "^1.1.13",
|
||||
"@radix-ui/react-toast": "^1.2.15",
|
||||
"@radix-ui/react-toggle": "^1.1.10",
|
||||
"@radix-ui/react-toggle-group": "^1.1.11",
|
||||
"@radix-ui/react-tooltip": "^1.2.8",
|
||||
"@hookform/resolvers": "5.7.1",
|
||||
"@radix-ui/react-accordion": "1.2.20",
|
||||
"@radix-ui/react-alert-dialog": "1.1.23",
|
||||
"@radix-ui/react-aspect-ratio": "1.1.15",
|
||||
"@radix-ui/react-avatar": "1.2.6",
|
||||
"@radix-ui/react-checkbox": "1.3.11",
|
||||
"@radix-ui/react-collapsible": "1.1.20",
|
||||
"@radix-ui/react-context-menu": "2.3.7",
|
||||
"@radix-ui/react-dialog": "1.1.23",
|
||||
"@radix-ui/react-dropdown-menu": "2.1.24",
|
||||
"@radix-ui/react-hover-card": "1.1.23",
|
||||
"@radix-ui/react-label": "2.1.15",
|
||||
"@radix-ui/react-menubar": "1.1.24",
|
||||
"@radix-ui/react-navigation-menu": "1.2.22",
|
||||
"@radix-ui/react-popover": "1.1.23",
|
||||
"@radix-ui/react-progress": "1.1.16",
|
||||
"@radix-ui/react-radio-group": "1.4.7",
|
||||
"@radix-ui/react-scroll-area": "1.2.18",
|
||||
"@radix-ui/react-select": "2.3.7",
|
||||
"@radix-ui/react-separator": "1.1.15",
|
||||
"@radix-ui/react-slider": "1.4.7",
|
||||
"@radix-ui/react-slot": "1.3.3",
|
||||
"@radix-ui/react-switch": "1.3.7",
|
||||
"@radix-ui/react-tabs": "1.1.21",
|
||||
"@radix-ui/react-toast": "1.2.23",
|
||||
"@radix-ui/react-toggle": "1.1.18",
|
||||
"@radix-ui/react-toggle-group": "1.1.19",
|
||||
"@radix-ui/react-tooltip": "1.2.16",
|
||||
"@replit/vite-plugin-cartographer": "catalog:",
|
||||
"@replit/vite-plugin-runtime-error-modal": "catalog:",
|
||||
"@tailwindcss/vite": "catalog:",
|
||||
@@ -45,29 +45,30 @@
|
||||
"@types/react": "catalog:",
|
||||
"@types/react-dom": "catalog:",
|
||||
"@vitejs/plugin-react": "catalog:",
|
||||
"chokidar": "^4.0.3",
|
||||
"chokidar": "5.0.0",
|
||||
"class-variance-authority": "catalog:",
|
||||
"clsx": "catalog:",
|
||||
"cmdk": "^1.1.1",
|
||||
"date-fns": "^3.6.0",
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"fast-glob": "^3.3.3",
|
||||
"cmdk": "1.1.1",
|
||||
"date-fns": "4.4.0",
|
||||
"embla-carousel-react": "8.6.0",
|
||||
"fast-glob": "3.3.3",
|
||||
"framer-motion": "catalog:",
|
||||
"input-otp": "^1.4.2",
|
||||
"input-otp": "1.4.2",
|
||||
"lucide-react": "catalog:",
|
||||
"next-themes": "^0.4.6",
|
||||
"next-themes": "0.4.6",
|
||||
"react": "catalog:",
|
||||
"react-day-picker": "^9.14.0",
|
||||
"react-day-picker": "10.0.1",
|
||||
"react-dom": "catalog:",
|
||||
"react-hook-form": "^7.75.0",
|
||||
"react-resizable-panels": "^2.1.9",
|
||||
"recharts": "^2.15.4",
|
||||
"sonner": "^2.0.7",
|
||||
"react-hook-form": "7.84.0",
|
||||
"react-is": "19.2.8",
|
||||
"react-resizable-panels": "4.12.2",
|
||||
"recharts": "3.10.1",
|
||||
"sonner": "2.0.7",
|
||||
"tailwind-merge": "catalog:",
|
||||
"tailwindcss": "catalog:",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"vaul": "^1.1.2",
|
||||
"tailwindcss-animate": "1.0.7",
|
||||
"tw-animate-css": "1.4.0",
|
||||
"vaul": "1.1.2",
|
||||
"vite": "catalog:",
|
||||
"zod": "catalog:"
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ function Calendar({
|
||||
: "[&>svg]:text-muted-foreground flex h-8 items-center gap-1 rounded-md pl-2 pr-1 text-sm [&>svg]:size-3.5",
|
||||
defaultClassNames.caption_label
|
||||
),
|
||||
table: "w-full border-collapse",
|
||||
month_grid: "w-full border-collapse",
|
||||
weekdays: cn("flex", defaultClassNames.weekdays),
|
||||
weekday: cn(
|
||||
"text-muted-foreground flex-1 select-none rounded-md text-[0.8rem] font-normal",
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as RechartsPrimitive from "recharts"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
// Format: { THEME_NAME: CSS_SELECTOR }
|
||||
const THEMES = { light: "", dark: ".dark" } as const
|
||||
|
||||
export type ChartConfig = {
|
||||
@@ -101,7 +102,7 @@ const ChartTooltip = RechartsPrimitive.Tooltip
|
||||
|
||||
const ChartTooltipContent = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
|
||||
RechartsPrimitive.TooltipContentProps &
|
||||
React.ComponentProps<"div"> & {
|
||||
hideLabel?: boolean
|
||||
hideIndicator?: boolean
|
||||
@@ -191,7 +192,7 @@ const ChartTooltipContent = React.forwardRef<
|
||||
|
||||
return (
|
||||
<div
|
||||
key={item.dataKey}
|
||||
key={typeof item.dataKey === "string" || typeof item.dataKey === "number" ? item.dataKey : index}
|
||||
className={cn(
|
||||
"flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
|
||||
indicator === "dot" && "items-center"
|
||||
@@ -260,7 +261,7 @@ const ChartLegend = RechartsPrimitive.Legend
|
||||
const ChartLegendContent = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.ComponentProps<"div"> &
|
||||
Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
|
||||
Pick<RechartsPrimitive.DefaultLegendContentProps, "payload" | "verticalAlign"> & {
|
||||
hideIcon?: boolean
|
||||
nameKey?: string
|
||||
}
|
||||
@@ -317,6 +318,7 @@ const ChartLegendContent = React.forwardRef<
|
||||
)
|
||||
ChartLegendContent.displayName = "ChartLegend"
|
||||
|
||||
// Helper to extract item config from a payload.
|
||||
function getPayloadConfigFromPayload(
|
||||
config: ChartConfig,
|
||||
payload: unknown,
|
||||
|
||||
@@ -8,10 +8,10 @@ import { cn } from "@/lib/utils"
|
||||
const ResizablePanelGroup = ({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) => (
|
||||
<ResizablePrimitive.PanelGroup
|
||||
}: React.ComponentProps<typeof ResizablePrimitive.Group>) => (
|
||||
<ResizablePrimitive.Group
|
||||
className={cn(
|
||||
"flex h-full w-full data-[panel-group-direction=vertical]:flex-col",
|
||||
"flex h-full w-full data-[group-orientation=vertical]:flex-col",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -24,12 +24,12 @@ const ResizableHandle = ({
|
||||
withHandle,
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & {
|
||||
}: React.ComponentProps<typeof ResizablePrimitive.Separator> & {
|
||||
withHandle?: boolean
|
||||
}) => (
|
||||
<ResizablePrimitive.PanelResizeHandle
|
||||
<ResizablePrimitive.Separator
|
||||
className={cn(
|
||||
"relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90",
|
||||
"relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[group-orientation=vertical]:h-px data-[group-orientation=vertical]:w-full data-[group-orientation=vertical]:after:left-0 data-[group-orientation=vertical]:after:h-1 data-[group-orientation=vertical]:after:w-full data-[group-orientation=vertical]:after:-translate-y-1/2 data-[group-orientation=vertical]:after:translate-x-0 [&[data-group-orientation=vertical]>div]:rotate-90",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -39,7 +39,7 @@ const ResizableHandle = ({
|
||||
<GripVertical className="h-2.5 w-2.5" />
|
||||
</div>
|
||||
)}
|
||||
</ResizablePrimitive.PanelResizeHandle>
|
||||
</ResizablePrimitive.Separator>
|
||||
)
|
||||
|
||||
export { ResizablePanelGroup, ResizablePanel, ResizableHandle }
|
||||
|
||||
@@ -10,38 +10,38 @@
|
||||
"typecheck": "tsc -p tsconfig.json --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hookform/resolvers": "^3.10.0",
|
||||
"@radix-ui/react-accordion": "^1.2.4",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.7",
|
||||
"@radix-ui/react-aspect-ratio": "^1.1.3",
|
||||
"@radix-ui/react-avatar": "^1.1.4",
|
||||
"@radix-ui/react-checkbox": "^1.1.5",
|
||||
"@radix-ui/react-collapsible": "^1.1.4",
|
||||
"@radix-ui/react-context-menu": "^2.2.7",
|
||||
"@radix-ui/react-dialog": "^1.1.7",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.7",
|
||||
"@radix-ui/react-hover-card": "^1.1.7",
|
||||
"@radix-ui/react-label": "^2.1.3",
|
||||
"@radix-ui/react-menubar": "^1.1.7",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.6",
|
||||
"@radix-ui/react-popover": "^1.1.7",
|
||||
"@radix-ui/react-progress": "^1.1.3",
|
||||
"@radix-ui/react-radio-group": "^1.2.4",
|
||||
"@radix-ui/react-scroll-area": "^1.2.4",
|
||||
"@radix-ui/react-select": "^2.1.7",
|
||||
"@radix-ui/react-separator": "^1.1.3",
|
||||
"@radix-ui/react-slider": "^1.2.4",
|
||||
"@radix-ui/react-slot": "^1.2.0",
|
||||
"@radix-ui/react-switch": "^1.1.4",
|
||||
"@radix-ui/react-tabs": "^1.1.4",
|
||||
"@radix-ui/react-toast": "^1.2.7",
|
||||
"@radix-ui/react-toggle": "^1.1.3",
|
||||
"@radix-ui/react-toggle-group": "^1.1.3",
|
||||
"@radix-ui/react-tooltip": "^1.2.0",
|
||||
"@hookform/resolvers": "5.7.1",
|
||||
"@radix-ui/react-accordion": "1.2.20",
|
||||
"@radix-ui/react-alert-dialog": "1.1.23",
|
||||
"@radix-ui/react-aspect-ratio": "1.1.15",
|
||||
"@radix-ui/react-avatar": "1.2.6",
|
||||
"@radix-ui/react-checkbox": "1.3.11",
|
||||
"@radix-ui/react-collapsible": "1.1.20",
|
||||
"@radix-ui/react-context-menu": "2.3.7",
|
||||
"@radix-ui/react-dialog": "1.1.23",
|
||||
"@radix-ui/react-dropdown-menu": "2.1.24",
|
||||
"@radix-ui/react-hover-card": "1.1.23",
|
||||
"@radix-ui/react-label": "2.1.15",
|
||||
"@radix-ui/react-menubar": "1.1.24",
|
||||
"@radix-ui/react-navigation-menu": "1.2.22",
|
||||
"@radix-ui/react-popover": "1.1.23",
|
||||
"@radix-ui/react-progress": "1.1.16",
|
||||
"@radix-ui/react-radio-group": "1.4.7",
|
||||
"@radix-ui/react-scroll-area": "1.2.18",
|
||||
"@radix-ui/react-select": "2.3.7",
|
||||
"@radix-ui/react-separator": "1.1.15",
|
||||
"@radix-ui/react-slider": "1.4.7",
|
||||
"@radix-ui/react-slot": "1.3.3",
|
||||
"@radix-ui/react-switch": "1.3.7",
|
||||
"@radix-ui/react-tabs": "1.1.21",
|
||||
"@radix-ui/react-toast": "1.2.23",
|
||||
"@radix-ui/react-toggle": "1.1.18",
|
||||
"@radix-ui/react-toggle-group": "1.1.19",
|
||||
"@radix-ui/react-tooltip": "1.2.16",
|
||||
"@replit/vite-plugin-cartographer": "catalog:",
|
||||
"@replit/vite-plugin-dev-banner": "catalog:",
|
||||
"@replit/vite-plugin-runtime-error-modal": "catalog:",
|
||||
"@tailwindcss/typography": "^0.5.15",
|
||||
"@tailwindcss/typography": "0.5.20",
|
||||
"@tailwindcss/vite": "catalog:",
|
||||
"@tanstack/react-query": "catalog:",
|
||||
"@tanstack/react-virtual": "catalog:",
|
||||
@@ -52,29 +52,30 @@
|
||||
"@workspace/api-client-react": "workspace:*",
|
||||
"class-variance-authority": "catalog:",
|
||||
"clsx": "catalog:",
|
||||
"cmdk": "^1.1.1",
|
||||
"date-fns": "^3.6.0",
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"cmdk": "1.1.1",
|
||||
"date-fns": "4.4.0",
|
||||
"embla-carousel-react": "8.6.0",
|
||||
"framer-motion": "catalog:",
|
||||
"i18next": "^26.3.6",
|
||||
"input-otp": "^1.4.2",
|
||||
"i18next": "26.3.6",
|
||||
"input-otp": "1.4.2",
|
||||
"lucide-react": "catalog:",
|
||||
"next-themes": "^0.4.6",
|
||||
"next-themes": "0.4.6",
|
||||
"react": "catalog:",
|
||||
"react-day-picker": "^9.11.1",
|
||||
"react-day-picker": "10.0.1",
|
||||
"react-dom": "catalog:",
|
||||
"react-hook-form": "^7.55.0",
|
||||
"react-i18next": "^17.0.11",
|
||||
"react-icons": "^5.4.0",
|
||||
"react-resizable-panels": "^2.1.7",
|
||||
"recharts": "^2.15.2",
|
||||
"sonner": "^2.0.7",
|
||||
"react-hook-form": "7.84.0",
|
||||
"react-i18next": "17.0.11",
|
||||
"react-icons": "5.7.0",
|
||||
"react-is": "19.2.8",
|
||||
"react-resizable-panels": "4.12.2",
|
||||
"recharts": "3.10.1",
|
||||
"sonner": "2.0.7",
|
||||
"tailwind-merge": "catalog:",
|
||||
"tailwindcss": "catalog:",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"vaul": "^1.1.2",
|
||||
"tw-animate-css": "1.4.0",
|
||||
"vaul": "1.1.2",
|
||||
"vite": "catalog:",
|
||||
"wouter": "^3.3.5",
|
||||
"wouter": "catalog:",
|
||||
"zod": "catalog:"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ function Calendar({
|
||||
: "[&>svg]:text-muted-foreground flex h-8 items-center gap-1 rounded-md pl-2 pr-1 text-sm [&>svg]:size-3.5",
|
||||
defaultClassNames.caption_label
|
||||
),
|
||||
table: "w-full border-collapse",
|
||||
month_grid: "w-full border-collapse",
|
||||
weekdays: cn("flex", defaultClassNames.weekdays),
|
||||
weekday: cn(
|
||||
"text-muted-foreground flex-1 select-none rounded-md text-[0.8rem] font-normal",
|
||||
|
||||
@@ -102,7 +102,7 @@ const ChartTooltip = RechartsPrimitive.Tooltip
|
||||
|
||||
const ChartTooltipContent = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
|
||||
RechartsPrimitive.TooltipContentProps &
|
||||
React.ComponentProps<"div"> & {
|
||||
hideLabel?: boolean
|
||||
hideIndicator?: boolean
|
||||
@@ -192,7 +192,7 @@ const ChartTooltipContent = React.forwardRef<
|
||||
|
||||
return (
|
||||
<div
|
||||
key={item.dataKey}
|
||||
key={typeof item.dataKey === "string" || typeof item.dataKey === "number" ? item.dataKey : index}
|
||||
className={cn(
|
||||
"flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
|
||||
indicator === "dot" && "items-center"
|
||||
@@ -261,7 +261,7 @@ const ChartLegend = RechartsPrimitive.Legend
|
||||
const ChartLegendContent = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.ComponentProps<"div"> &
|
||||
Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
|
||||
Pick<RechartsPrimitive.DefaultLegendContentProps, "payload" | "verticalAlign"> & {
|
||||
hideIcon?: boolean
|
||||
nameKey?: string
|
||||
}
|
||||
|
||||
@@ -8,10 +8,10 @@ import { cn } from "@/lib/utils"
|
||||
const ResizablePanelGroup = ({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) => (
|
||||
<ResizablePrimitive.PanelGroup
|
||||
}: React.ComponentProps<typeof ResizablePrimitive.Group>) => (
|
||||
<ResizablePrimitive.Group
|
||||
className={cn(
|
||||
"flex h-full w-full data-[panel-group-direction=vertical]:flex-col",
|
||||
"flex h-full w-full data-[group-orientation=vertical]:flex-col",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -24,12 +24,12 @@ const ResizableHandle = ({
|
||||
withHandle,
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & {
|
||||
}: React.ComponentProps<typeof ResizablePrimitive.Separator> & {
|
||||
withHandle?: boolean
|
||||
}) => (
|
||||
<ResizablePrimitive.PanelResizeHandle
|
||||
<ResizablePrimitive.Separator
|
||||
className={cn(
|
||||
"relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90",
|
||||
"relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[group-orientation=vertical]:h-px data-[group-orientation=vertical]:w-full data-[group-orientation=vertical]:after:left-0 data-[group-orientation=vertical]:after:h-1 data-[group-orientation=vertical]:after:w-full data-[group-orientation=vertical]:after:-translate-y-1/2 data-[group-orientation=vertical]:after:translate-x-0 [&[data-group-orientation=vertical]>div]:rotate-90",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -39,7 +39,7 @@ const ResizableHandle = ({
|
||||
<GripVertical className="h-2.5 w-2.5" />
|
||||
</div>
|
||||
)}
|
||||
</ResizablePrimitive.PanelResizeHandle>
|
||||
</ResizablePrimitive.Separator>
|
||||
)
|
||||
|
||||
export { ResizablePanelGroup, ResizablePanel, ResizableHandle }
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# Dependency Policy
|
||||
|
||||
How this workspace keeps npm dependencies current, safe and reproducible.
|
||||
|
||||
## Principles
|
||||
|
||||
1. **Exact pins** – Every direct dependency in `package.json` is pinned to an
|
||||
exact version (`1.2.3`, never `^1.2.3`). `save-exact=true` is set in
|
||||
`.npmrc` so `pnpm add` follows this rule automatically.
|
||||
2. **Committed lockfile** – `pnpm-lock.yaml` is committed. CI and the Docker
|
||||
build install with `--frozen-lockfile`, so builds are reproducible.
|
||||
3. **Security gates** – CI runs `pnpm audit --prod` (fails on any finding) and
|
||||
`pnpm audit --audit-level high` (fails on high/critical). A non-zero exit
|
||||
blocks the release pipeline.
|
||||
4. **Supply-chain protection** – `minimumReleaseAge: 1440` (1 day) in
|
||||
`pnpm-workspace.yaml` blocks freshly published versions. Do not lower or
|
||||
disable it; only allowlist trusted publishers via
|
||||
`minimumReleaseAgeExclude` for urgent security fixes.
|
||||
5. **Pinned critical packages** – `react`, `react-dom` and `esbuild` are
|
||||
intentionally excluded from automated updates (see `renovate.json`). Bump
|
||||
them deliberately, one release at a time, with a test pass.
|
||||
|
||||
## Update cadence
|
||||
|
||||
| Frequency | Scope | Who |
|
||||
|-----------|-------|-----|
|
||||
| Weekly | Patch + minor (grouped by Renovate) | Renovate PR, human merge after green CI |
|
||||
| Monthly | One major version at a time | Human, own commit + release tag |
|
||||
| As needed | Security advisories | Immediate fix + patch release |
|
||||
| Yearly | Infrastructure review (Node LTS, Postgres major, k3s) | Human |
|
||||
|
||||
## Rules
|
||||
|
||||
- **Patch/minor**: merge freely once CI (typecheck + build + audit) is green.
|
||||
- **Major**: never bundle multiple majors into one release. One major per
|
||||
commit so regressions can be bisected to the responsible change.
|
||||
- **"Safe intermediate"**: if a package has a newer major that is not yet
|
||||
absorbed, stay on the latest patch/minor of the *current* major line. The
|
||||
exact-pin guarantees we never float into a new major by accident.
|
||||
- **Node/Infra**: Node base image and pnpm version in the `Dockerfile` are
|
||||
pinned exactly. Update them together with a build + live smoke test.
|
||||
- **Postgres**: image tag is managed in the `admin/apps` repository
|
||||
(`apps/system/toolrate`). Major upgrades run through a backup/restore flow.
|
||||
|
||||
## Process for applying updates
|
||||
|
||||
1. `pnpm install` to refresh the lockfile.
|
||||
2. Regenerate clients if the API changed: `pnpm --filter @workspace/api-spec run codegen`.
|
||||
3. Run `pnpm run typecheck`, `pnpm run build` (with `PORT=8080 BASE_PATH=/`),
|
||||
and `pnpm audit`.
|
||||
4. Fix any code that the new majors require (the common ones are
|
||||
`openid-client`, `recharts`, `react-day-picker`, `date-fns`,
|
||||
`@hookform/resolvers`, `react-resizable-panels`).
|
||||
5. Commit, tag `vX.Y.Z`, push. CI builds, audits and deploys to k3s.
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
@@ -70,6 +70,21 @@ type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
|
||||
|
||||
|
||||
|
||||
const withQueryKey = <T extends object, K>(query: T, queryKey: K): T & { queryKey: K } => {
|
||||
const result = { queryKey } as T & { queryKey: K };
|
||||
for (const key of Object.keys(query)) {
|
||||
// The explicit queryKey always wins, matching the previous
|
||||
// `{ ...query, queryKey }` spread where it was set last.
|
||||
if (key === 'queryKey') continue;
|
||||
Object.defineProperty(result, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: () => (query as Record<string, unknown>)[key],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export const getHealthCheckUrl = () => {
|
||||
|
||||
|
||||
@@ -82,7 +97,7 @@ export const getHealthCheckUrl = () => {
|
||||
* Returns server health status
|
||||
* @summary Health check
|
||||
*/
|
||||
export const healthCheck = async ( options?: RequestInit): Promise<HealthStatus> => {
|
||||
export const healthCheck = async ( options?: Parameters<typeof customFetch>[1]): Promise<HealthStatus> => {
|
||||
|
||||
return customFetch<HealthStatus>(getHealthCheckUrl(),
|
||||
{
|
||||
@@ -139,7 +154,7 @@ export function useHealthCheck<TData = Awaited<ReturnType<typeof healthCheck>>,
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & { queryKey: QueryKey };
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -160,7 +175,7 @@ export const getGetVersionUrl = () => {
|
||||
* Returns the running build version, commit SHA and build date
|
||||
* @summary Build version information
|
||||
*/
|
||||
export const getVersion = async ( options?: RequestInit): Promise<VersionInfo> => {
|
||||
export const getVersion = async ( options?: Parameters<typeof customFetch>[1]): Promise<VersionInfo> => {
|
||||
|
||||
return customFetch<VersionInfo>(getGetVersionUrl(),
|
||||
{
|
||||
@@ -217,7 +232,7 @@ export function useGetVersion<TData = Awaited<ReturnType<typeof getVersion>>, TE
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & { queryKey: QueryKey };
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -232,7 +247,7 @@ export const getListToolsUrl = (params?: ListToolsParams,) => {
|
||||
Object.entries(params || {}).forEach(([key, value]) => {
|
||||
|
||||
if (value !== undefined) {
|
||||
normalizedParams.append(key, value === null ? 'null' : value.toString())
|
||||
normalizedParams.append(key, value === null ? 'null' : String(value))
|
||||
}
|
||||
});
|
||||
|
||||
@@ -244,7 +259,7 @@ export const getListToolsUrl = (params?: ListToolsParams,) => {
|
||||
/**
|
||||
* @summary List all tools
|
||||
*/
|
||||
export const listTools = async (params?: ListToolsParams, options?: RequestInit): Promise<ToolWithStats[]> => {
|
||||
export const listTools = async (params?: ListToolsParams, options?: Parameters<typeof customFetch>[1]): Promise<ToolWithStats[]> => {
|
||||
|
||||
return customFetch<ToolWithStats[]>(getListToolsUrl(params),
|
||||
{
|
||||
@@ -301,7 +316,7 @@ export function useListTools<TData = Awaited<ReturnType<typeof listTools>>, TErr
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & { queryKey: QueryKey };
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -321,21 +336,21 @@ export const getCreateToolUrl = () => {
|
||||
/**
|
||||
* @summary Create a new tool
|
||||
*/
|
||||
export const createTool = async (toolInput: ToolInput, options?: RequestInit): Promise<Tool> => {
|
||||
export const createTool = async (toolInput: ToolInput, options?: Parameters<typeof customFetch>[1]): Promise<Tool> => {
|
||||
|
||||
return customFetch<Tool>(getCreateToolUrl(),
|
||||
{
|
||||
...options,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', ...options?.headers },
|
||||
body: JSON.stringify(
|
||||
toolInput,)
|
||||
body: JSON.stringify(toolInput)
|
||||
}
|
||||
);}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const getCreateToolMutationOptions = <TError = ErrorType<ErrorResponse>,
|
||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof createTool>>, TError,{data: BodyType<ToolInput>}, TContext>, request?: SecondParameter<typeof customFetch>}
|
||||
): UseMutationOptions<Awaited<ReturnType<typeof createTool>>, TError,{data: BodyType<ToolInput>}, TContext> => {
|
||||
@@ -387,7 +402,7 @@ export const getListCompareToolsUrl = (params: ListCompareToolsParams,) => {
|
||||
Object.entries(params || {}).forEach(([key, value]) => {
|
||||
|
||||
if (value !== undefined) {
|
||||
normalizedParams.append(key, value === null ? 'null' : value.toString())
|
||||
normalizedParams.append(key, value === null ? 'null' : String(value))
|
||||
}
|
||||
});
|
||||
|
||||
@@ -399,7 +414,7 @@ export const getListCompareToolsUrl = (params: ListCompareToolsParams,) => {
|
||||
/**
|
||||
* @summary Compare tools side by side (premium)
|
||||
*/
|
||||
export const listCompareTools = async (params: ListCompareToolsParams, options?: RequestInit): Promise<ToolWithStats[]> => {
|
||||
export const listCompareTools = async (params: ListCompareToolsParams, options?: Parameters<typeof customFetch>[1]): Promise<ToolWithStats[]> => {
|
||||
|
||||
return customFetch<ToolWithStats[]>(getListCompareToolsUrl(params),
|
||||
{
|
||||
@@ -456,7 +471,7 @@ export function useListCompareTools<TData = Awaited<ReturnType<typeof listCompar
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & { queryKey: QueryKey };
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -476,7 +491,7 @@ export const getGetToolRatingHistoryUrl = (id: number,) => {
|
||||
/**
|
||||
* @summary Get a tool's rating history over time
|
||||
*/
|
||||
export const getToolRatingHistory = async (id: number, options?: RequestInit): Promise<RatingHistoryItem[]> => {
|
||||
export const getToolRatingHistory = async (id: number, options?: Parameters<typeof customFetch>[1]): Promise<RatingHistoryItem[]> => {
|
||||
|
||||
return customFetch<RatingHistoryItem[]>(getGetToolRatingHistoryUrl(id),
|
||||
{
|
||||
@@ -513,7 +528,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {};
|
||||
|
||||
|
||||
|
||||
return { queryKey, queryFn, enabled: !!(id), ...queryOptions} as UseQueryOptions<Awaited<ReturnType<typeof getToolRatingHistory>>, TError, TData> & { queryKey: QueryKey }
|
||||
return { queryKey, queryFn, enabled: id !== null && id !== undefined, ...queryOptions} as UseQueryOptions<Awaited<ReturnType<typeof getToolRatingHistory>>, TError, TData> & { queryKey: QueryKey }
|
||||
}
|
||||
|
||||
export type GetToolRatingHistoryQueryResult = NonNullable<Awaited<ReturnType<typeof getToolRatingHistory>>>
|
||||
@@ -533,7 +548,7 @@ export function useGetToolRatingHistory<TData = Awaited<ReturnType<typeof getToo
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & { queryKey: QueryKey };
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -553,7 +568,7 @@ export const getGetToolUrl = (id: number,) => {
|
||||
/**
|
||||
* @summary Get a tool by ID
|
||||
*/
|
||||
export const getTool = async (id: number, options?: RequestInit): Promise<ToolWithStats> => {
|
||||
export const getTool = async (id: number, options?: Parameters<typeof customFetch>[1]): Promise<ToolWithStats> => {
|
||||
|
||||
return customFetch<ToolWithStats>(getGetToolUrl(id),
|
||||
{
|
||||
@@ -590,7 +605,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {};
|
||||
|
||||
|
||||
|
||||
return { queryKey, queryFn, enabled: !!(id), ...queryOptions} as UseQueryOptions<Awaited<ReturnType<typeof getTool>>, TError, TData> & { queryKey: QueryKey }
|
||||
return { queryKey, queryFn, enabled: id !== null && id !== undefined, ...queryOptions} as UseQueryOptions<Awaited<ReturnType<typeof getTool>>, TError, TData> & { queryKey: QueryKey }
|
||||
}
|
||||
|
||||
export type GetToolQueryResult = NonNullable<Awaited<ReturnType<typeof getTool>>>
|
||||
@@ -610,7 +625,7 @@ export function useGetTool<TData = Awaited<ReturnType<typeof getTool>>, TError =
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & { queryKey: QueryKey };
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -631,21 +646,21 @@ export const getUpdateToolUrl = (id: number,) => {
|
||||
* @summary Update a tool
|
||||
*/
|
||||
export const updateTool = async (id: number,
|
||||
toolUpdate: ToolUpdate, options?: RequestInit): Promise<Tool> => {
|
||||
toolUpdate: ToolUpdate, options?: Parameters<typeof customFetch>[1]): Promise<Tool> => {
|
||||
|
||||
return customFetch<Tool>(getUpdateToolUrl(id),
|
||||
{
|
||||
...options,
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json', ...options?.headers },
|
||||
body: JSON.stringify(
|
||||
toolUpdate,)
|
||||
body: JSON.stringify(toolUpdate)
|
||||
}
|
||||
);}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const getUpdateToolMutationOptions = <TError = ErrorType<ErrorResponse>,
|
||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof updateTool>>, TError,{id: number;data: BodyType<ToolUpdate>}, TContext>, request?: SecondParameter<typeof customFetch>}
|
||||
): UseMutationOptions<Awaited<ReturnType<typeof updateTool>>, TError,{id: number;data: BodyType<ToolUpdate>}, TContext> => {
|
||||
@@ -702,7 +717,7 @@ export const getDeleteToolUrl = (id: number,) => {
|
||||
/**
|
||||
* @summary Delete a tool
|
||||
*/
|
||||
export const deleteTool = async (id: number, options?: RequestInit): Promise<void> => {
|
||||
export const deleteTool = async (id: number, options?: Parameters<typeof customFetch>[1]): Promise<void> => {
|
||||
|
||||
return customFetch<void>(getDeleteToolUrl(id),
|
||||
{
|
||||
@@ -716,6 +731,7 @@ export const deleteTool = async (id: number, options?: RequestInit): Promise<voi
|
||||
|
||||
|
||||
|
||||
|
||||
export const getDeleteToolMutationOptions = <TError = ErrorType<ErrorResponse>,
|
||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof deleteTool>>, TError,{id: number}, TContext>, request?: SecondParameter<typeof customFetch>}
|
||||
): UseMutationOptions<Awaited<ReturnType<typeof deleteTool>>, TError,{id: number}, TContext> => {
|
||||
@@ -767,7 +783,7 @@ export const getListTrashedToolsUrl = (params?: ListTrashedToolsParams,) => {
|
||||
Object.entries(params || {}).forEach(([key, value]) => {
|
||||
|
||||
if (value !== undefined) {
|
||||
normalizedParams.append(key, value === null ? 'null' : value.toString())
|
||||
normalizedParams.append(key, value === null ? 'null' : String(value))
|
||||
}
|
||||
});
|
||||
|
||||
@@ -779,7 +795,7 @@ export const getListTrashedToolsUrl = (params?: ListTrashedToolsParams,) => {
|
||||
/**
|
||||
* @summary List trashed (soft-deleted) tools
|
||||
*/
|
||||
export const listTrashedTools = async (params?: ListTrashedToolsParams, options?: RequestInit): Promise<Tool[]> => {
|
||||
export const listTrashedTools = async (params?: ListTrashedToolsParams, options?: Parameters<typeof customFetch>[1]): Promise<Tool[]> => {
|
||||
|
||||
return customFetch<Tool[]>(getListTrashedToolsUrl(params),
|
||||
{
|
||||
@@ -836,7 +852,7 @@ export function useListTrashedTools<TData = Awaited<ReturnType<typeof listTrashe
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & { queryKey: QueryKey };
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -856,21 +872,21 @@ export const getTrashToolsUrl = () => {
|
||||
/**
|
||||
* @summary Move tools to trash (admin)
|
||||
*/
|
||||
export const trashTools = async (trashToolsInput: TrashToolsInput, options?: RequestInit): Promise<TrashTools200> => {
|
||||
export const trashTools = async (trashToolsInput: TrashToolsInput, options?: Parameters<typeof customFetch>[1]): Promise<TrashTools200> => {
|
||||
|
||||
return customFetch<TrashTools200>(getTrashToolsUrl(),
|
||||
{
|
||||
...options,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', ...options?.headers },
|
||||
body: JSON.stringify(
|
||||
trashToolsInput,)
|
||||
body: JSON.stringify(trashToolsInput)
|
||||
}
|
||||
);}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const getTrashToolsMutationOptions = <TError = ErrorType<ErrorResponse>,
|
||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof trashTools>>, TError,{data: BodyType<TrashToolsInput>}, TContext>, request?: SecondParameter<typeof customFetch>}
|
||||
): UseMutationOptions<Awaited<ReturnType<typeof trashTools>>, TError,{data: BodyType<TrashToolsInput>}, TContext> => {
|
||||
@@ -927,21 +943,21 @@ export const getDeleteTrashedToolsUrl = () => {
|
||||
/**
|
||||
* @summary Permanently delete trashed tools (admin)
|
||||
*/
|
||||
export const deleteTrashedTools = async (trashToolsInput: TrashToolsInput, options?: RequestInit): Promise<void> => {
|
||||
export const deleteTrashedTools = async (trashToolsInput: TrashToolsInput, options?: Parameters<typeof customFetch>[1]): Promise<void> => {
|
||||
|
||||
return customFetch<void>(getDeleteTrashedToolsUrl(),
|
||||
{
|
||||
...options,
|
||||
method: 'DELETE',
|
||||
headers: { 'Content-Type': 'application/json', ...options?.headers },
|
||||
body: JSON.stringify(
|
||||
trashToolsInput,)
|
||||
body: JSON.stringify(trashToolsInput)
|
||||
}
|
||||
);}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const getDeleteTrashedToolsMutationOptions = <TError = ErrorType<ErrorResponse>,
|
||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof deleteTrashedTools>>, TError,{data: BodyType<TrashToolsInput>}, TContext>, request?: SecondParameter<typeof customFetch>}
|
||||
): UseMutationOptions<Awaited<ReturnType<typeof deleteTrashedTools>>, TError,{data: BodyType<TrashToolsInput>}, TContext> => {
|
||||
@@ -998,21 +1014,21 @@ export const getRestoreToolsUrl = () => {
|
||||
/**
|
||||
* @summary Restore trashed tools
|
||||
*/
|
||||
export const restoreTools = async (trashToolsInput: TrashToolsInput, options?: RequestInit): Promise<RestoreTools200> => {
|
||||
export const restoreTools = async (trashToolsInput: TrashToolsInput, options?: Parameters<typeof customFetch>[1]): Promise<RestoreTools200> => {
|
||||
|
||||
return customFetch<RestoreTools200>(getRestoreToolsUrl(),
|
||||
{
|
||||
...options,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', ...options?.headers },
|
||||
body: JSON.stringify(
|
||||
trashToolsInput,)
|
||||
body: JSON.stringify(trashToolsInput)
|
||||
}
|
||||
);}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const getRestoreToolsMutationOptions = <TError = ErrorType<ErrorResponse>,
|
||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof restoreTools>>, TError,{data: BodyType<TrashToolsInput>}, TContext>, request?: SecondParameter<typeof customFetch>}
|
||||
): UseMutationOptions<Awaited<ReturnType<typeof restoreTools>>, TError,{data: BodyType<TrashToolsInput>}, TContext> => {
|
||||
@@ -1069,7 +1085,7 @@ export const getEmptyTrashUrl = () => {
|
||||
/**
|
||||
* @summary Permanently delete all trashed tools (admin)
|
||||
*/
|
||||
export const emptyTrash = async ( options?: RequestInit): Promise<EmptyTrash200> => {
|
||||
export const emptyTrash = async ( options?: Parameters<typeof customFetch>[1]): Promise<EmptyTrash200> => {
|
||||
|
||||
return customFetch<EmptyTrash200>(getEmptyTrashUrl(),
|
||||
{
|
||||
@@ -1083,6 +1099,7 @@ export const emptyTrash = async ( options?: RequestInit): Promise<EmptyTrash200>
|
||||
|
||||
|
||||
|
||||
|
||||
export const getEmptyTrashMutationOptions = <TError = ErrorType<ErrorResponse>,
|
||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof emptyTrash>>, TError,void, TContext>, request?: SecondParameter<typeof customFetch>}
|
||||
): UseMutationOptions<Awaited<ReturnType<typeof emptyTrash>>, TError,void, TContext> => {
|
||||
@@ -1139,7 +1156,7 @@ export const getListToolRatingsUrl = (id: number,) => {
|
||||
/**
|
||||
* @summary List ratings for a tool
|
||||
*/
|
||||
export const listToolRatings = async (id: number, options?: RequestInit): Promise<Rating[]> => {
|
||||
export const listToolRatings = async (id: number, options?: Parameters<typeof customFetch>[1]): Promise<Rating[]> => {
|
||||
|
||||
return customFetch<Rating[]>(getListToolRatingsUrl(id),
|
||||
{
|
||||
@@ -1176,7 +1193,7 @@ const {query: queryOptions, request: requestOptions} = options ?? {};
|
||||
|
||||
|
||||
|
||||
return { queryKey, queryFn, enabled: !!(id), ...queryOptions} as UseQueryOptions<Awaited<ReturnType<typeof listToolRatings>>, TError, TData> & { queryKey: QueryKey }
|
||||
return { queryKey, queryFn, enabled: id !== null && id !== undefined, ...queryOptions} as UseQueryOptions<Awaited<ReturnType<typeof listToolRatings>>, TError, TData> & { queryKey: QueryKey }
|
||||
}
|
||||
|
||||
export type ListToolRatingsQueryResult = NonNullable<Awaited<ReturnType<typeof listToolRatings>>>
|
||||
@@ -1196,7 +1213,7 @@ export function useListToolRatings<TData = Awaited<ReturnType<typeof listToolRat
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & { queryKey: QueryKey };
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -1217,21 +1234,21 @@ export const getCreateRatingUrl = (id: number,) => {
|
||||
* @summary Submit a rating for a tool
|
||||
*/
|
||||
export const createRating = async (id: number,
|
||||
ratingInput: RatingInput, options?: RequestInit): Promise<Rating> => {
|
||||
ratingInput: RatingInput, options?: Parameters<typeof customFetch>[1]): Promise<Rating> => {
|
||||
|
||||
return customFetch<Rating>(getCreateRatingUrl(id),
|
||||
{
|
||||
...options,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', ...options?.headers },
|
||||
body: JSON.stringify(
|
||||
ratingInput,)
|
||||
body: JSON.stringify(ratingInput)
|
||||
}
|
||||
);}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const getCreateRatingMutationOptions = <TError = ErrorType<ErrorResponse>,
|
||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof createRating>>, TError,{id: number;data: BodyType<RatingInput>}, TContext>, request?: SecondParameter<typeof customFetch>}
|
||||
): UseMutationOptions<Awaited<ReturnType<typeof createRating>>, TError,{id: number;data: BodyType<RatingInput>}, TContext> => {
|
||||
@@ -1288,7 +1305,7 @@ export const getGetAnalyticsSummaryUrl = () => {
|
||||
/**
|
||||
* @summary Overall platform statistics
|
||||
*/
|
||||
export const getAnalyticsSummary = async ( options?: RequestInit): Promise<AnalyticsSummary> => {
|
||||
export const getAnalyticsSummary = async ( options?: Parameters<typeof customFetch>[1]): Promise<AnalyticsSummary> => {
|
||||
|
||||
return customFetch<AnalyticsSummary>(getGetAnalyticsSummaryUrl(),
|
||||
{
|
||||
@@ -1345,7 +1362,7 @@ export function useGetAnalyticsSummary<TData = Awaited<ReturnType<typeof getAnal
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & { queryKey: QueryKey };
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -1360,7 +1377,7 @@ export const getGetTopToolsUrl = (params?: GetTopToolsParams,) => {
|
||||
Object.entries(params || {}).forEach(([key, value]) => {
|
||||
|
||||
if (value !== undefined) {
|
||||
normalizedParams.append(key, value === null ? 'null' : value.toString())
|
||||
normalizedParams.append(key, value === null ? 'null' : String(value))
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1372,7 +1389,7 @@ export const getGetTopToolsUrl = (params?: GetTopToolsParams,) => {
|
||||
/**
|
||||
* @summary Top-rated tools
|
||||
*/
|
||||
export const getTopTools = async (params?: GetTopToolsParams, options?: RequestInit): Promise<TopToolEntry[]> => {
|
||||
export const getTopTools = async (params?: GetTopToolsParams, options?: Parameters<typeof customFetch>[1]): Promise<TopToolEntry[]> => {
|
||||
|
||||
return customFetch<TopToolEntry[]>(getGetTopToolsUrl(params),
|
||||
{
|
||||
@@ -1429,7 +1446,7 @@ export function useGetTopTools<TData = Awaited<ReturnType<typeof getTopTools>>,
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & { queryKey: QueryKey };
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -1449,7 +1466,7 @@ export const getGetAnalyticsByCategoryUrl = () => {
|
||||
/**
|
||||
* @summary Rating statistics grouped by category
|
||||
*/
|
||||
export const getAnalyticsByCategory = async ( options?: RequestInit): Promise<CategoryStats[]> => {
|
||||
export const getAnalyticsByCategory = async ( options?: Parameters<typeof customFetch>[1]): Promise<CategoryStats[]> => {
|
||||
|
||||
return customFetch<CategoryStats[]>(getGetAnalyticsByCategoryUrl(),
|
||||
{
|
||||
@@ -1506,7 +1523,7 @@ export function useGetAnalyticsByCategory<TData = Awaited<ReturnType<typeof getA
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & { queryKey: QueryKey };
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -1521,7 +1538,7 @@ export const getGetRatingDistributionUrl = (params?: GetRatingDistributionParams
|
||||
Object.entries(params || {}).forEach(([key, value]) => {
|
||||
|
||||
if (value !== undefined) {
|
||||
normalizedParams.append(key, value === null ? 'null' : value.toString())
|
||||
normalizedParams.append(key, value === null ? 'null' : String(value))
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1533,7 +1550,7 @@ export const getGetRatingDistributionUrl = (params?: GetRatingDistributionParams
|
||||
/**
|
||||
* @summary Distribution of rating scores across the platform
|
||||
*/
|
||||
export const getRatingDistribution = async (params?: GetRatingDistributionParams, options?: RequestInit): Promise<RatingDistribution> => {
|
||||
export const getRatingDistribution = async (params?: GetRatingDistributionParams, options?: Parameters<typeof customFetch>[1]): Promise<RatingDistribution> => {
|
||||
|
||||
return customFetch<RatingDistribution>(getGetRatingDistributionUrl(params),
|
||||
{
|
||||
@@ -1590,7 +1607,7 @@ export function useGetRatingDistribution<TData = Awaited<ReturnType<typeof getRa
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & { queryKey: QueryKey };
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -1610,7 +1627,7 @@ export const getListCategoriesUrl = () => {
|
||||
/**
|
||||
* @summary List all distinct tool categories
|
||||
*/
|
||||
export const listCategories = async ( options?: RequestInit): Promise<string[]> => {
|
||||
export const listCategories = async ( options?: Parameters<typeof customFetch>[1]): Promise<string[]> => {
|
||||
|
||||
return customFetch<string[]>(getListCategoriesUrl(),
|
||||
{
|
||||
@@ -1667,7 +1684,7 @@ export function useListCategories<TData = Awaited<ReturnType<typeof listCategori
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & { queryKey: QueryKey };
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -1687,7 +1704,7 @@ export const getListAllFeaturesUrl = () => {
|
||||
/**
|
||||
* @summary List all distinct feature strings across all tools
|
||||
*/
|
||||
export const listAllFeatures = async ( options?: RequestInit): Promise<string[]> => {
|
||||
export const listAllFeatures = async ( options?: Parameters<typeof customFetch>[1]): Promise<string[]> => {
|
||||
|
||||
return customFetch<string[]>(getListAllFeaturesUrl(),
|
||||
{
|
||||
@@ -1744,7 +1761,7 @@ export function useListAllFeatures<TData = Awaited<ReturnType<typeof listAllFeat
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & { queryKey: QueryKey };
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -1764,7 +1781,7 @@ export const getListAllTagsUrl = () => {
|
||||
/**
|
||||
* @summary List all distinct tag strings across all tools
|
||||
*/
|
||||
export const listAllTags = async ( options?: RequestInit): Promise<string[]> => {
|
||||
export const listAllTags = async ( options?: Parameters<typeof customFetch>[1]): Promise<string[]> => {
|
||||
|
||||
return customFetch<string[]>(getListAllTagsUrl(),
|
||||
{
|
||||
@@ -1821,7 +1838,7 @@ export function useListAllTags<TData = Awaited<ReturnType<typeof listAllTags>>,
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & { queryKey: QueryKey };
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -1841,7 +1858,7 @@ export const getGetAuthModeUrl = () => {
|
||||
/**
|
||||
* @summary Get authentication mode (oidc or local)
|
||||
*/
|
||||
export const getAuthMode = async ( options?: RequestInit): Promise<AuthMode> => {
|
||||
export const getAuthMode = async ( options?: Parameters<typeof customFetch>[1]): Promise<AuthMode> => {
|
||||
|
||||
return customFetch<AuthMode>(getGetAuthModeUrl(),
|
||||
{
|
||||
@@ -1898,7 +1915,7 @@ export function useGetAuthMode<TData = Awaited<ReturnType<typeof getAuthMode>>,
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & { queryKey: QueryKey };
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -1918,7 +1935,7 @@ export const getGetCsrfTokenUrl = () => {
|
||||
/**
|
||||
* @summary Get a CSRF token for state-changing requests
|
||||
*/
|
||||
export const getCsrfToken = async ( options?: RequestInit): Promise<CsrfToken> => {
|
||||
export const getCsrfToken = async ( options?: Parameters<typeof customFetch>[1]): Promise<CsrfToken> => {
|
||||
|
||||
return customFetch<CsrfToken>(getGetCsrfTokenUrl(),
|
||||
{
|
||||
@@ -1975,7 +1992,7 @@ export function useGetCsrfToken<TData = Awaited<ReturnType<typeof getCsrfToken>>
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & { queryKey: QueryKey };
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -1995,21 +2012,21 @@ export const getLocalLoginUrl = () => {
|
||||
/**
|
||||
* @summary Local username/password login
|
||||
*/
|
||||
export const localLogin = async (localLoginInput: LocalLoginInput, options?: RequestInit): Promise<AuthUser> => {
|
||||
export const localLogin = async (localLoginInput: LocalLoginInput, options?: Parameters<typeof customFetch>[1]): Promise<AuthUser> => {
|
||||
|
||||
return customFetch<AuthUser>(getLocalLoginUrl(),
|
||||
{
|
||||
...options,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', ...options?.headers },
|
||||
body: JSON.stringify(
|
||||
localLoginInput,)
|
||||
body: JSON.stringify(localLoginInput)
|
||||
}
|
||||
);}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const getLocalLoginMutationOptions = <TError = ErrorType<ErrorResponse>,
|
||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof localLogin>>, TError,{data: BodyType<LocalLoginInput>}, TContext>, request?: SecondParameter<typeof customFetch>}
|
||||
): UseMutationOptions<Awaited<ReturnType<typeof localLogin>>, TError,{data: BodyType<LocalLoginInput>}, TContext> => {
|
||||
@@ -2066,7 +2083,7 @@ export const getGetMeUrl = () => {
|
||||
/**
|
||||
* @summary Get current authenticated user
|
||||
*/
|
||||
export const getMe = async ( options?: RequestInit): Promise<AuthUser> => {
|
||||
export const getMe = async ( options?: Parameters<typeof customFetch>[1]): Promise<AuthUser> => {
|
||||
|
||||
return customFetch<AuthUser>(getGetMeUrl(),
|
||||
{
|
||||
@@ -2123,7 +2140,7 @@ export function useGetMe<TData = Awaited<ReturnType<typeof getMe>>, TError = Err
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & { queryKey: QueryKey };
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -2143,21 +2160,21 @@ export const getChangeMyPasswordUrl = () => {
|
||||
/**
|
||||
* @summary Change own password (local users only)
|
||||
*/
|
||||
export const changeMyPassword = async (changePasswordInput: ChangePasswordInput, options?: RequestInit): Promise<void> => {
|
||||
export const changeMyPassword = async (changePasswordInput: ChangePasswordInput, options?: Parameters<typeof customFetch>[1]): Promise<void> => {
|
||||
|
||||
return customFetch<void>(getChangeMyPasswordUrl(),
|
||||
{
|
||||
...options,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', ...options?.headers },
|
||||
body: JSON.stringify(
|
||||
changePasswordInput,)
|
||||
body: JSON.stringify(changePasswordInput)
|
||||
}
|
||||
);}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const getChangeMyPasswordMutationOptions = <TError = ErrorType<ErrorResponse>,
|
||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof changeMyPassword>>, TError,{data: BodyType<ChangePasswordInput>}, TContext>, request?: SecondParameter<typeof customFetch>}
|
||||
): UseMutationOptions<Awaited<ReturnType<typeof changeMyPassword>>, TError,{data: BodyType<ChangePasswordInput>}, TContext> => {
|
||||
@@ -2214,7 +2231,7 @@ export const getGetPasswordRedirectUrl = () => {
|
||||
/**
|
||||
* @summary Get redirect URL for managing credentials in the identity provider
|
||||
*/
|
||||
export const getPasswordRedirect = async ( options?: RequestInit): Promise<PasswordRedirect> => {
|
||||
export const getPasswordRedirect = async ( options?: Parameters<typeof customFetch>[1]): Promise<PasswordRedirect> => {
|
||||
|
||||
return customFetch<PasswordRedirect>(getGetPasswordRedirectUrl(),
|
||||
{
|
||||
@@ -2271,7 +2288,7 @@ export function useGetPasswordRedirect<TData = Awaited<ReturnType<typeof getPass
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & { queryKey: QueryKey };
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -2291,7 +2308,7 @@ export const getGetMePreferencesUrl = () => {
|
||||
/**
|
||||
* @summary Get current user's browse preferences
|
||||
*/
|
||||
export const getMePreferences = async ( options?: RequestInit): Promise<UserPreferences> => {
|
||||
export const getMePreferences = async ( options?: Parameters<typeof customFetch>[1]): Promise<UserPreferences> => {
|
||||
|
||||
return customFetch<UserPreferences>(getGetMePreferencesUrl(),
|
||||
{
|
||||
@@ -2348,7 +2365,7 @@ export function useGetMePreferences<TData = Awaited<ReturnType<typeof getMePrefe
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & { queryKey: QueryKey };
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -2368,21 +2385,21 @@ export const getUpdateMePreferencesUrl = () => {
|
||||
/**
|
||||
* @summary Update current user's browse preferences
|
||||
*/
|
||||
export const updateMePreferences = async (userPreferences: UserPreferences, options?: RequestInit): Promise<UserPreferences> => {
|
||||
export const updateMePreferences = async (userPreferences: UserPreferences, options?: Parameters<typeof customFetch>[1]): Promise<UserPreferences> => {
|
||||
|
||||
return customFetch<UserPreferences>(getUpdateMePreferencesUrl(),
|
||||
{
|
||||
...options,
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json', ...options?.headers },
|
||||
body: JSON.stringify(
|
||||
userPreferences,)
|
||||
body: JSON.stringify(userPreferences)
|
||||
}
|
||||
);}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const getUpdateMePreferencesMutationOptions = <TError = ErrorType<ErrorResponse>,
|
||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof updateMePreferences>>, TError,{data: BodyType<UserPreferences>}, TContext>, request?: SecondParameter<typeof customFetch>}
|
||||
): UseMutationOptions<Awaited<ReturnType<typeof updateMePreferences>>, TError,{data: BodyType<UserPreferences>}, TContext> => {
|
||||
@@ -2439,7 +2456,7 @@ export const getGetMeWatchlistUrl = () => {
|
||||
/**
|
||||
* @summary Get current user's watchlist tools (premium)
|
||||
*/
|
||||
export const getMeWatchlist = async ( options?: RequestInit): Promise<ToolWithStats[]> => {
|
||||
export const getMeWatchlist = async ( options?: Parameters<typeof customFetch>[1]): Promise<ToolWithStats[]> => {
|
||||
|
||||
return customFetch<ToolWithStats[]>(getGetMeWatchlistUrl(),
|
||||
{
|
||||
@@ -2496,7 +2513,7 @@ export function useGetMeWatchlist<TData = Awaited<ReturnType<typeof getMeWatchli
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & { queryKey: QueryKey };
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -2516,7 +2533,7 @@ export const getListUsersUrl = () => {
|
||||
/**
|
||||
* @summary List all local users (admin only)
|
||||
*/
|
||||
export const listUsers = async ( options?: RequestInit): Promise<User[]> => {
|
||||
export const listUsers = async ( options?: Parameters<typeof customFetch>[1]): Promise<User[]> => {
|
||||
|
||||
return customFetch<User[]>(getListUsersUrl(),
|
||||
{
|
||||
@@ -2573,7 +2590,7 @@ export function useListUsers<TData = Awaited<ReturnType<typeof listUsers>>, TErr
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & { queryKey: QueryKey };
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -2593,21 +2610,21 @@ export const getCreateUserUrl = () => {
|
||||
/**
|
||||
* @summary Create a new local user (admin only)
|
||||
*/
|
||||
export const createUser = async (userCreateInput: UserCreateInput, options?: RequestInit): Promise<User> => {
|
||||
export const createUser = async (userCreateInput: UserCreateInput, options?: Parameters<typeof customFetch>[1]): Promise<User> => {
|
||||
|
||||
return customFetch<User>(getCreateUserUrl(),
|
||||
{
|
||||
...options,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', ...options?.headers },
|
||||
body: JSON.stringify(
|
||||
userCreateInput,)
|
||||
body: JSON.stringify(userCreateInput)
|
||||
}
|
||||
);}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const getCreateUserMutationOptions = <TError = ErrorType<ErrorResponse>,
|
||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof createUser>>, TError,{data: BodyType<UserCreateInput>}, TContext>, request?: SecondParameter<typeof customFetch>}
|
||||
): UseMutationOptions<Awaited<ReturnType<typeof createUser>>, TError,{data: BodyType<UserCreateInput>}, TContext> => {
|
||||
@@ -2665,21 +2682,21 @@ export const getUpdateUserUrl = (id: number,) => {
|
||||
* @summary Update user role (admin only)
|
||||
*/
|
||||
export const updateUser = async (id: number,
|
||||
userRoleUpdate: UserRoleUpdate, options?: RequestInit): Promise<User> => {
|
||||
userRoleUpdate: UserRoleUpdate, options?: Parameters<typeof customFetch>[1]): Promise<User> => {
|
||||
|
||||
return customFetch<User>(getUpdateUserUrl(id),
|
||||
{
|
||||
...options,
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json', ...options?.headers },
|
||||
body: JSON.stringify(
|
||||
userRoleUpdate,)
|
||||
body: JSON.stringify(userRoleUpdate)
|
||||
}
|
||||
);}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const getUpdateUserMutationOptions = <TError = ErrorType<ErrorResponse>,
|
||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof updateUser>>, TError,{id: number;data: BodyType<UserRoleUpdate>}, TContext>, request?: SecondParameter<typeof customFetch>}
|
||||
): UseMutationOptions<Awaited<ReturnType<typeof updateUser>>, TError,{id: number;data: BodyType<UserRoleUpdate>}, TContext> => {
|
||||
@@ -2736,7 +2753,7 @@ export const getDeleteUserUrl = (id: number,) => {
|
||||
/**
|
||||
* @summary Delete a user (admin only)
|
||||
*/
|
||||
export const deleteUser = async (id: number, options?: RequestInit): Promise<void> => {
|
||||
export const deleteUser = async (id: number, options?: Parameters<typeof customFetch>[1]): Promise<void> => {
|
||||
|
||||
return customFetch<void>(getDeleteUserUrl(id),
|
||||
{
|
||||
@@ -2750,6 +2767,7 @@ export const deleteUser = async (id: number, options?: RequestInit): Promise<voi
|
||||
|
||||
|
||||
|
||||
|
||||
export const getDeleteUserMutationOptions = <TError = ErrorType<unknown>,
|
||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof deleteUser>>, TError,{id: number}, TContext>, request?: SecondParameter<typeof customFetch>}
|
||||
): UseMutationOptions<Awaited<ReturnType<typeof deleteUser>>, TError,{id: number}, TContext> => {
|
||||
@@ -2807,21 +2825,21 @@ export const getSetUserPasswordUrl = (id: number,) => {
|
||||
* @summary Set/reset a user's password (admin only, local users only)
|
||||
*/
|
||||
export const setUserPassword = async (id: number,
|
||||
setPasswordInput: SetPasswordInput, options?: RequestInit): Promise<void> => {
|
||||
setPasswordInput: SetPasswordInput, options?: Parameters<typeof customFetch>[1]): Promise<void> => {
|
||||
|
||||
return customFetch<void>(getSetUserPasswordUrl(id),
|
||||
{
|
||||
...options,
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json', ...options?.headers },
|
||||
body: JSON.stringify(
|
||||
setPasswordInput,)
|
||||
body: JSON.stringify(setPasswordInput)
|
||||
}
|
||||
);}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const getSetUserPasswordMutationOptions = <TError = ErrorType<ErrorResponse>,
|
||||
TContext = unknown>(options?: { mutation?:UseMutationOptions<Awaited<ReturnType<typeof setUserPassword>>, TError,{id: number;data: BodyType<SetPasswordInput>}, TContext>, request?: SecondParameter<typeof customFetch>}
|
||||
): UseMutationOptions<Awaited<ReturnType<typeof setUserPassword>>, TError,{id: number;data: BodyType<SetPasswordInput>}, TContext> => {
|
||||
@@ -2873,7 +2891,7 @@ export const getListAuditLogsUrl = (params?: ListAuditLogsParams,) => {
|
||||
Object.entries(params || {}).forEach(([key, value]) => {
|
||||
|
||||
if (value !== undefined) {
|
||||
normalizedParams.append(key, value === null ? 'null' : value.toString())
|
||||
normalizedParams.append(key, value === null ? 'null' : String(value))
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2885,7 +2903,7 @@ export const getListAuditLogsUrl = (params?: ListAuditLogsParams,) => {
|
||||
/**
|
||||
* @summary List audit log entries (admin only)
|
||||
*/
|
||||
export const listAuditLogs = async (params?: ListAuditLogsParams, options?: RequestInit): Promise<AuditLog[]> => {
|
||||
export const listAuditLogs = async (params?: ListAuditLogsParams, options?: Parameters<typeof customFetch>[1]): Promise<AuditLog[]> => {
|
||||
|
||||
return customFetch<AuditLog[]>(getListAuditLogsUrl(params),
|
||||
{
|
||||
@@ -2942,7 +2960,7 @@ export function useListAuditLogs<TData = Awaited<ReturnType<typeof listAuditLogs
|
||||
|
||||
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & { queryKey: QueryKey };
|
||||
|
||||
return { ...query, queryKey: queryOptions.queryKey };
|
||||
return withQueryKey(query, queryOptions.queryKey);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
"codegen": "orval --config ./orval.config.ts && pnpm -w run typecheck:libs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"orval": "^8.9.1"
|
||||
"orval": "8.23.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
@@ -25,7 +25,7 @@ export const GetVersionResponse = zod.object({
|
||||
"version": zod.string(),
|
||||
"commitSha": zod.string().nullish(),
|
||||
"buildDate": zod.string().nullish(),
|
||||
"trashRetentionDays": zod.number().optional()
|
||||
"trashRetentionDays": zod.int().optional()
|
||||
})
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ export const ListToolsQueryParams = zod.object({
|
||||
})
|
||||
|
||||
export const ListToolsResponseItem = zod.object({
|
||||
"id": zod.number(),
|
||||
"id": zod.int(),
|
||||
"name": zod.string(),
|
||||
"description": zod.string(),
|
||||
"category": zod.string(),
|
||||
@@ -58,7 +58,7 @@ export const ListToolsResponseItem = zod.object({
|
||||
"tags": zod.array(zod.string()).optional(),
|
||||
"createdAt": zod.coerce.date(),
|
||||
"updatedAt": zod.coerce.date(),
|
||||
"ratingCount": zod.number(),
|
||||
"ratingCount": zod.int(),
|
||||
"avgUsefulness": zod.number().nullable(),
|
||||
"avgUsability": zod.number().nullable(),
|
||||
"avgCombined": zod.number().nullable()
|
||||
@@ -84,16 +84,8 @@ export const CreateToolBody = zod.object({
|
||||
"tags": zod.array(zod.string()).optional()
|
||||
})
|
||||
|
||||
|
||||
/**
|
||||
* @summary Compare tools side by side (premium)
|
||||
*/
|
||||
export const ListCompareToolsQueryParams = zod.object({
|
||||
"ids": zod.coerce.string().describe('Comma-separated tool ids')
|
||||
})
|
||||
|
||||
export const ListCompareToolsResponseItem = zod.object({
|
||||
"id": zod.number(),
|
||||
export const CreateToolResponse = zod.object({
|
||||
"id": zod.int(),
|
||||
"name": zod.string(),
|
||||
"description": zod.string(),
|
||||
"category": zod.string(),
|
||||
@@ -104,7 +96,31 @@ export const ListCompareToolsResponseItem = zod.object({
|
||||
"tags": zod.array(zod.string()).optional(),
|
||||
"createdAt": zod.coerce.date(),
|
||||
"updatedAt": zod.coerce.date(),
|
||||
"ratingCount": zod.number(),
|
||||
"deletedAt": zod.coerce.date().nullish(),
|
||||
"deletedBy": zod.string().nullish()
|
||||
})
|
||||
|
||||
|
||||
/**
|
||||
* @summary Compare tools side by side (premium)
|
||||
*/
|
||||
export const ListCompareToolsQueryParams = zod.object({
|
||||
"ids": zod.coerce.string().describe('Comma-separated tool ids')
|
||||
})
|
||||
|
||||
export const ListCompareToolsResponseItem = zod.object({
|
||||
"id": zod.int(),
|
||||
"name": zod.string(),
|
||||
"description": zod.string(),
|
||||
"category": zod.string(),
|
||||
"websiteUrl": zod.string().nullish(),
|
||||
"iconUrl": zod.string().nullish(),
|
||||
"createdBy": zod.string().nullish(),
|
||||
"features": zod.array(zod.string()).optional(),
|
||||
"tags": zod.array(zod.string()).optional(),
|
||||
"createdAt": zod.coerce.date(),
|
||||
"updatedAt": zod.coerce.date(),
|
||||
"ratingCount": zod.int(),
|
||||
"avgUsefulness": zod.number().nullable(),
|
||||
"avgUsability": zod.number().nullable(),
|
||||
"avgCombined": zod.number().nullable()
|
||||
@@ -116,7 +132,7 @@ export const ListCompareToolsResponse = zod.array(ListCompareToolsResponseItem)
|
||||
* @summary Get a tool's rating history over time
|
||||
*/
|
||||
export const GetToolRatingHistoryParams = zod.object({
|
||||
"id": zod.coerce.number()
|
||||
"id": zod.coerce.number().int()
|
||||
})
|
||||
|
||||
export const GetToolRatingHistoryResponseItem = zod.object({
|
||||
@@ -132,11 +148,11 @@ export const GetToolRatingHistoryResponse = zod.array(GetToolRatingHistoryRespon
|
||||
* @summary Get a tool by ID
|
||||
*/
|
||||
export const GetToolParams = zod.object({
|
||||
"id": zod.coerce.number()
|
||||
"id": zod.coerce.number().int()
|
||||
})
|
||||
|
||||
export const GetToolResponse = zod.object({
|
||||
"id": zod.number(),
|
||||
"id": zod.int(),
|
||||
"name": zod.string(),
|
||||
"description": zod.string(),
|
||||
"category": zod.string(),
|
||||
@@ -147,7 +163,7 @@ export const GetToolResponse = zod.object({
|
||||
"tags": zod.array(zod.string()).optional(),
|
||||
"createdAt": zod.coerce.date(),
|
||||
"updatedAt": zod.coerce.date(),
|
||||
"ratingCount": zod.number(),
|
||||
"ratingCount": zod.int(),
|
||||
"avgUsefulness": zod.number().nullable(),
|
||||
"avgUsability": zod.number().nullable(),
|
||||
"avgCombined": zod.number().nullable()
|
||||
@@ -158,7 +174,7 @@ export const GetToolResponse = zod.object({
|
||||
* @summary Update a tool
|
||||
*/
|
||||
export const UpdateToolParams = zod.object({
|
||||
"id": zod.coerce.number()
|
||||
"id": zod.coerce.number().int()
|
||||
})
|
||||
|
||||
|
||||
@@ -175,7 +191,7 @@ export const UpdateToolBody = zod.object({
|
||||
})
|
||||
|
||||
export const UpdateToolResponse = zod.object({
|
||||
"id": zod.number(),
|
||||
"id": zod.int(),
|
||||
"name": zod.string(),
|
||||
"description": zod.string(),
|
||||
"category": zod.string(),
|
||||
@@ -195,9 +211,11 @@ export const UpdateToolResponse = zod.object({
|
||||
* @summary Delete a tool
|
||||
*/
|
||||
export const DeleteToolParams = zod.object({
|
||||
"id": zod.coerce.number()
|
||||
"id": zod.coerce.number().int()
|
||||
})
|
||||
|
||||
export const DeleteToolResponse = zod.void()
|
||||
|
||||
|
||||
/**
|
||||
* @summary List trashed (soft-deleted) tools
|
||||
@@ -207,7 +225,7 @@ export const ListTrashedToolsQueryParams = zod.object({
|
||||
})
|
||||
|
||||
export const ListTrashedToolsResponseItem = zod.object({
|
||||
"id": zod.number(),
|
||||
"id": zod.int(),
|
||||
"name": zod.string(),
|
||||
"description": zod.string(),
|
||||
"category": zod.string(),
|
||||
@@ -232,11 +250,11 @@ export const trashToolsBodyIdsMax = 500;
|
||||
|
||||
|
||||
export const TrashToolsBody = zod.object({
|
||||
"ids": zod.array(zod.number()).min(1).max(trashToolsBodyIdsMax)
|
||||
"ids": zod.array(zod.int()).min(1).max(trashToolsBodyIdsMax)
|
||||
})
|
||||
|
||||
export const TrashToolsResponse = zod.object({
|
||||
"trashed": zod.number().optional()
|
||||
"trashed": zod.int().optional()
|
||||
})
|
||||
|
||||
|
||||
@@ -248,9 +266,11 @@ export const deleteTrashedToolsBodyIdsMax = 500;
|
||||
|
||||
|
||||
export const DeleteTrashedToolsBody = zod.object({
|
||||
"ids": zod.array(zod.number()).min(1).max(deleteTrashedToolsBodyIdsMax)
|
||||
"ids": zod.array(zod.int()).min(1).max(deleteTrashedToolsBodyIdsMax)
|
||||
})
|
||||
|
||||
export const DeleteTrashedToolsResponse = zod.void()
|
||||
|
||||
|
||||
/**
|
||||
* @summary Restore trashed tools
|
||||
@@ -260,11 +280,11 @@ export const restoreToolsBodyIdsMax = 500;
|
||||
|
||||
|
||||
export const RestoreToolsBody = zod.object({
|
||||
"ids": zod.array(zod.number()).min(1).max(restoreToolsBodyIdsMax)
|
||||
"ids": zod.array(zod.int()).min(1).max(restoreToolsBodyIdsMax)
|
||||
})
|
||||
|
||||
export const RestoreToolsResponse = zod.object({
|
||||
"restored": zod.number().optional()
|
||||
"restored": zod.int().optional()
|
||||
})
|
||||
|
||||
|
||||
@@ -272,7 +292,7 @@ export const RestoreToolsResponse = zod.object({
|
||||
* @summary Permanently delete all trashed tools (admin)
|
||||
*/
|
||||
export const EmptyTrashResponse = zod.object({
|
||||
"deleted": zod.number().optional()
|
||||
"deleted": zod.int().optional()
|
||||
})
|
||||
|
||||
|
||||
@@ -280,7 +300,7 @@ export const EmptyTrashResponse = zod.object({
|
||||
* @summary List ratings for a tool
|
||||
*/
|
||||
export const ListToolRatingsParams = zod.object({
|
||||
"id": zod.coerce.number()
|
||||
"id": zod.coerce.number().int()
|
||||
})
|
||||
|
||||
export const listToolRatingsResponseUsefulnessMax = 5;
|
||||
@@ -290,10 +310,10 @@ export const listToolRatingsResponseUsabilityMax = 5;
|
||||
|
||||
|
||||
export const ListToolRatingsResponseItem = zod.object({
|
||||
"id": zod.number(),
|
||||
"toolId": zod.number(),
|
||||
"usefulness": zod.number().min(1).max(listToolRatingsResponseUsefulnessMax),
|
||||
"usability": zod.number().min(1).max(listToolRatingsResponseUsabilityMax),
|
||||
"id": zod.int(),
|
||||
"toolId": zod.int(),
|
||||
"usefulness": zod.int().min(1).max(listToolRatingsResponseUsefulnessMax),
|
||||
"usability": zod.int().min(1).max(listToolRatingsResponseUsabilityMax),
|
||||
"comment": zod.string().nullish(),
|
||||
"reviewerName": zod.string().nullish(),
|
||||
"createdAt": zod.coerce.date()
|
||||
@@ -305,7 +325,7 @@ export const ListToolRatingsResponse = zod.array(ListToolRatingsResponseItem)
|
||||
* @summary Submit a rating for a tool
|
||||
*/
|
||||
export const CreateRatingParams = zod.object({
|
||||
"id": zod.coerce.number()
|
||||
"id": zod.coerce.number().int()
|
||||
})
|
||||
|
||||
export const createRatingBodyUsefulnessMax = 5;
|
||||
@@ -315,25 +335,41 @@ export const createRatingBodyUsabilityMax = 5;
|
||||
|
||||
|
||||
export const CreateRatingBody = zod.object({
|
||||
"usefulness": zod.number().min(1).max(createRatingBodyUsefulnessMax),
|
||||
"usability": zod.number().min(1).max(createRatingBodyUsabilityMax),
|
||||
"usefulness": zod.int().min(1).max(createRatingBodyUsefulnessMax),
|
||||
"usability": zod.int().min(1).max(createRatingBodyUsabilityMax),
|
||||
"comment": zod.string().optional(),
|
||||
"reviewerName": zod.string().optional()
|
||||
})
|
||||
|
||||
export const createRatingResponseUsefulnessMax = 5;
|
||||
|
||||
export const createRatingResponseUsabilityMax = 5;
|
||||
|
||||
|
||||
|
||||
export const CreateRatingResponse = zod.object({
|
||||
"id": zod.int(),
|
||||
"toolId": zod.int(),
|
||||
"usefulness": zod.int().min(1).max(createRatingResponseUsefulnessMax),
|
||||
"usability": zod.int().min(1).max(createRatingResponseUsabilityMax),
|
||||
"comment": zod.string().nullish(),
|
||||
"reviewerName": zod.string().nullish(),
|
||||
"createdAt": zod.coerce.date()
|
||||
})
|
||||
|
||||
|
||||
/**
|
||||
* @summary Overall platform statistics
|
||||
*/
|
||||
export const GetAnalyticsSummaryResponse = zod.object({
|
||||
"totalTools": zod.number(),
|
||||
"totalRatings": zod.number(),
|
||||
"totalTools": zod.int(),
|
||||
"totalRatings": zod.int(),
|
||||
"avgUsefulness": zod.number().nullable(),
|
||||
"avgUsability": zod.number().nullable(),
|
||||
"avgCombined": zod.number().nullable(),
|
||||
"categoriesCount": zod.number(),
|
||||
"categoriesCount": zod.int(),
|
||||
"mostRatedTool": zod.object({
|
||||
"id": zod.number(),
|
||||
"id": zod.int(),
|
||||
"name": zod.string(),
|
||||
"description": zod.string(),
|
||||
"category": zod.string(),
|
||||
@@ -344,7 +380,7 @@ export const GetAnalyticsSummaryResponse = zod.object({
|
||||
"tags": zod.array(zod.string()).optional(),
|
||||
"createdAt": zod.coerce.date(),
|
||||
"updatedAt": zod.coerce.date(),
|
||||
"ratingCount": zod.number(),
|
||||
"ratingCount": zod.int(),
|
||||
"avgUsefulness": zod.number().nullable(),
|
||||
"avgUsability": zod.number().nullable(),
|
||||
"avgCombined": zod.number().nullable()
|
||||
@@ -356,13 +392,13 @@ export const GetAnalyticsSummaryResponse = zod.object({
|
||||
* @summary Top-rated tools
|
||||
*/
|
||||
export const GetTopToolsQueryParams = zod.object({
|
||||
"limit": zod.coerce.number().optional(),
|
||||
"limit": zod.coerce.number().int().optional(),
|
||||
"metric": zod.enum(['usefulness', 'usability', 'combined']).optional()
|
||||
})
|
||||
|
||||
export const GetTopToolsResponseItem = zod.object({
|
||||
"tool": zod.object({
|
||||
"id": zod.number(),
|
||||
"id": zod.int(),
|
||||
"name": zod.string(),
|
||||
"description": zod.string(),
|
||||
"category": zod.string(),
|
||||
@@ -373,13 +409,13 @@ export const GetTopToolsResponseItem = zod.object({
|
||||
"tags": zod.array(zod.string()).optional(),
|
||||
"createdAt": zod.coerce.date(),
|
||||
"updatedAt": zod.coerce.date(),
|
||||
"ratingCount": zod.number(),
|
||||
"ratingCount": zod.int(),
|
||||
"avgUsefulness": zod.number().nullable(),
|
||||
"avgUsability": zod.number().nullable(),
|
||||
"avgCombined": zod.number().nullable()
|
||||
}),
|
||||
"score": zod.number(),
|
||||
"ratingCount": zod.number()
|
||||
"ratingCount": zod.int()
|
||||
})
|
||||
export const GetTopToolsResponse = zod.array(GetTopToolsResponseItem)
|
||||
|
||||
@@ -389,8 +425,8 @@ export const GetTopToolsResponse = zod.array(GetTopToolsResponseItem)
|
||||
*/
|
||||
export const GetAnalyticsByCategoryResponseItem = zod.object({
|
||||
"category": zod.string(),
|
||||
"toolCount": zod.number(),
|
||||
"totalRatings": zod.number(),
|
||||
"toolCount": zod.int(),
|
||||
"totalRatings": zod.int(),
|
||||
"avgUsefulness": zod.number().nullable(),
|
||||
"avgUsability": zod.number().nullable()
|
||||
})
|
||||
@@ -401,17 +437,17 @@ export const GetAnalyticsByCategoryResponse = zod.array(GetAnalyticsByCategoryRe
|
||||
* @summary Distribution of rating scores across the platform
|
||||
*/
|
||||
export const GetRatingDistributionQueryParams = zod.object({
|
||||
"toolId": zod.coerce.number().optional()
|
||||
"toolId": zod.coerce.number().int().optional()
|
||||
})
|
||||
|
||||
export const GetRatingDistributionResponse = zod.object({
|
||||
"usefulness": zod.array(zod.object({
|
||||
"score": zod.number(),
|
||||
"count": zod.number()
|
||||
"score": zod.int(),
|
||||
"count": zod.int()
|
||||
})),
|
||||
"usability": zod.array(zod.object({
|
||||
"score": zod.number(),
|
||||
"count": zod.number()
|
||||
"score": zod.int(),
|
||||
"count": zod.int()
|
||||
}))
|
||||
})
|
||||
|
||||
@@ -501,6 +537,8 @@ export const ChangeMyPasswordBody = zod.object({
|
||||
"newPassword": zod.string().min(changeMyPasswordBodyNewPasswordMin)
|
||||
})
|
||||
|
||||
export const ChangeMyPasswordResponse = zod.void()
|
||||
|
||||
|
||||
/**
|
||||
* @summary Get redirect URL for managing credentials in the identity provider
|
||||
@@ -516,7 +554,7 @@ export const GetPasswordRedirectResponse = zod.object({
|
||||
export const GetMePreferencesResponse = zod.object({
|
||||
"view": zod.enum(['grid', 'table', 'rows']).optional(),
|
||||
"density": zod.enum(['cozy', 'compact']).optional(),
|
||||
"watchlist": zod.array(zod.number()).optional()
|
||||
"watchlist": zod.array(zod.int()).optional()
|
||||
})
|
||||
|
||||
|
||||
@@ -526,13 +564,13 @@ export const GetMePreferencesResponse = zod.object({
|
||||
export const UpdateMePreferencesBody = zod.object({
|
||||
"view": zod.enum(['grid', 'table', 'rows']).optional(),
|
||||
"density": zod.enum(['cozy', 'compact']).optional(),
|
||||
"watchlist": zod.array(zod.number()).optional()
|
||||
"watchlist": zod.array(zod.int()).optional()
|
||||
})
|
||||
|
||||
export const UpdateMePreferencesResponse = zod.object({
|
||||
"view": zod.enum(['grid', 'table', 'rows']).optional(),
|
||||
"density": zod.enum(['cozy', 'compact']).optional(),
|
||||
"watchlist": zod.array(zod.number()).optional()
|
||||
"watchlist": zod.array(zod.int()).optional()
|
||||
})
|
||||
|
||||
|
||||
@@ -540,7 +578,7 @@ export const UpdateMePreferencesResponse = zod.object({
|
||||
* @summary Get current user's watchlist tools (premium)
|
||||
*/
|
||||
export const GetMeWatchlistResponseItem = zod.object({
|
||||
"id": zod.number(),
|
||||
"id": zod.int(),
|
||||
"name": zod.string(),
|
||||
"description": zod.string(),
|
||||
"category": zod.string(),
|
||||
@@ -551,7 +589,7 @@ export const GetMeWatchlistResponseItem = zod.object({
|
||||
"tags": zod.array(zod.string()).optional(),
|
||||
"createdAt": zod.coerce.date(),
|
||||
"updatedAt": zod.coerce.date(),
|
||||
"ratingCount": zod.number(),
|
||||
"ratingCount": zod.int(),
|
||||
"avgUsefulness": zod.number().nullable(),
|
||||
"avgUsability": zod.number().nullable(),
|
||||
"avgCombined": zod.number().nullable()
|
||||
@@ -565,7 +603,7 @@ export const GetMeWatchlistResponse = zod.array(GetMeWatchlistResponseItem)
|
||||
export const listUsersResponseAuthProviderDefault = `local`;
|
||||
|
||||
export const ListUsersResponseItem = zod.object({
|
||||
"id": zod.number(),
|
||||
"id": zod.int(),
|
||||
"username": zod.string(),
|
||||
"email": zod.string().nullish(),
|
||||
"role": zod.enum(['admin', 'user']),
|
||||
@@ -593,12 +631,24 @@ export const CreateUserBody = zod.object({
|
||||
"tier": zod.enum(['free', 'premium', 'enterprise']).optional()
|
||||
})
|
||||
|
||||
export const createUserResponseAuthProviderDefault = `local`;
|
||||
|
||||
export const CreateUserResponse = zod.object({
|
||||
"id": zod.int(),
|
||||
"username": zod.string(),
|
||||
"email": zod.string().nullish(),
|
||||
"role": zod.enum(['admin', 'user']),
|
||||
"tier": zod.enum(['free', 'premium', 'enterprise']).optional(),
|
||||
"authProvider": zod.enum(['local', 'oidc']).default(createUserResponseAuthProviderDefault),
|
||||
"createdAt": zod.coerce.date()
|
||||
})
|
||||
|
||||
|
||||
/**
|
||||
* @summary Update user role (admin only)
|
||||
*/
|
||||
export const UpdateUserParams = zod.object({
|
||||
"id": zod.coerce.number()
|
||||
"id": zod.coerce.number().int()
|
||||
})
|
||||
|
||||
export const UpdateUserBody = zod.object({
|
||||
@@ -609,7 +659,7 @@ export const UpdateUserBody = zod.object({
|
||||
export const updateUserResponseAuthProviderDefault = `local`;
|
||||
|
||||
export const UpdateUserResponse = zod.object({
|
||||
"id": zod.number(),
|
||||
"id": zod.int(),
|
||||
"username": zod.string(),
|
||||
"email": zod.string().nullish(),
|
||||
"role": zod.enum(['admin', 'user']),
|
||||
@@ -623,15 +673,17 @@ export const UpdateUserResponse = zod.object({
|
||||
* @summary Delete a user (admin only)
|
||||
*/
|
||||
export const DeleteUserParams = zod.object({
|
||||
"id": zod.coerce.number()
|
||||
"id": zod.coerce.number().int()
|
||||
})
|
||||
|
||||
export const DeleteUserResponse = zod.void()
|
||||
|
||||
|
||||
/**
|
||||
* @summary Set/reset a user's password (admin only, local users only)
|
||||
*/
|
||||
export const SetUserPasswordParams = zod.object({
|
||||
"id": zod.coerce.number()
|
||||
"id": zod.coerce.number().int()
|
||||
})
|
||||
|
||||
export const setUserPasswordBodyPasswordMin = 6;
|
||||
@@ -642,20 +694,22 @@ export const SetUserPasswordBody = zod.object({
|
||||
"password": zod.string().min(setUserPasswordBodyPasswordMin)
|
||||
})
|
||||
|
||||
export const SetUserPasswordResponse = zod.void()
|
||||
|
||||
|
||||
/**
|
||||
* @summary List audit log entries (admin only)
|
||||
*/
|
||||
export const ListAuditLogsQueryParams = zod.object({
|
||||
"entityType": zod.coerce.string().optional(),
|
||||
"entityId": zod.coerce.number().optional(),
|
||||
"limit": zod.coerce.number().optional()
|
||||
"entityId": zod.coerce.number().int().optional(),
|
||||
"limit": zod.coerce.number().int().optional()
|
||||
})
|
||||
|
||||
export const ListAuditLogsResponseItem = zod.object({
|
||||
"id": zod.number(),
|
||||
"id": zod.int(),
|
||||
"entityType": zod.string(),
|
||||
"entityId": zod.number().nullish(),
|
||||
"entityId": zod.int().nullish(),
|
||||
"action": zod.string(),
|
||||
"userId": zod.string(),
|
||||
"username": zod.string(),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Generated by orval v8.9.1 🍺
|
||||
* Generated by orval v8.23.0 🍺
|
||||
* Do not edit manually.
|
||||
* Api
|
||||
* ToolRate API — Tool listing and rating platform
|
||||
|
||||
+4
-4
@@ -13,13 +13,13 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"drizzle-orm": "catalog:",
|
||||
"drizzle-zod": "^0.8.3",
|
||||
"pg": "^8.20.0",
|
||||
"drizzle-zod": "0.8.3",
|
||||
"pg": "8.22.0",
|
||||
"zod": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "catalog:",
|
||||
"@types/pg": "^8.20.0",
|
||||
"drizzle-kit": "^0.31.10"
|
||||
"@types/pg": "8.20.3",
|
||||
"drizzle-kit": "0.31.10"
|
||||
}
|
||||
}
|
||||
|
||||
+6
-17
@@ -10,24 +10,13 @@
|
||||
},
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"prettier": "^3.8.3",
|
||||
"typescript": "~5.9.3"
|
||||
},
|
||||
"pnpm": {
|
||||
"supportedArchitectures": {
|
||||
"os": [
|
||||
"current",
|
||||
"linux"
|
||||
],
|
||||
"cpu": [
|
||||
"current",
|
||||
"x64"
|
||||
]
|
||||
}
|
||||
"prettier": "3.9.6",
|
||||
"typescript": "7.0.2"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-darwin-arm64": "^4.62.4",
|
||||
"@tailwindcss/oxide-darwin-arm64": "^4.3.3",
|
||||
"lightningcss-darwin-arm64": "^1.33.0"
|
||||
"@esbuild/darwin-arm64": "0.28.1",
|
||||
"@rollup/rollup-darwin-arm64": "4.62.4",
|
||||
"@tailwindcss/oxide-darwin-arm64": "4.3.3",
|
||||
"lightningcss-darwin-arm64": "1.33.0"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+2498
-1931
File diff suppressed because it is too large
Load Diff
+49
-33
@@ -27,6 +27,14 @@
|
||||
# ============================================================================
|
||||
minimumReleaseAge: 1440
|
||||
|
||||
supportedArchitectures:
|
||||
os:
|
||||
- current
|
||||
- linux
|
||||
cpu:
|
||||
- current
|
||||
- x64
|
||||
|
||||
minimumReleaseAgeExclude:
|
||||
# Exclude @replit scoped packages from the minimum release age check.
|
||||
# These are published by Replit and trusted — the supply-chain attack vector
|
||||
@@ -41,39 +49,40 @@ packages:
|
||||
- scripts
|
||||
|
||||
catalog:
|
||||
'@replit/vite-plugin-cartographer': ^0.5.1
|
||||
'@replit/vite-plugin-dev-banner': ^0.1.1
|
||||
'@replit/vite-plugin-runtime-error-modal': ^0.0.6
|
||||
'@tailwindcss/vite': ^4.1.14
|
||||
'@tanstack/react-query': ^5.90.21
|
||||
'@tanstack/react-virtual': ^3.13.6
|
||||
'@types/node': ^25.3.3
|
||||
'@types/react': ^19.2.0
|
||||
'@types/react-dom': ^19.2.0
|
||||
'@vitejs/plugin-react': ^5.0.4
|
||||
class-variance-authority: ^0.7.1
|
||||
clsx: ^2.1.1
|
||||
drizzle-orm: ^0.45.2
|
||||
framer-motion: ^12.23.24
|
||||
lucide-react: ^0.545.0
|
||||
# Must be this exact version because expo requires it
|
||||
react: 19.1.0
|
||||
# Must be this exact version because expo requires it
|
||||
react-dom: 19.1.0
|
||||
tailwind-merge: ^3.3.1
|
||||
tailwindcss: ^4.1.14
|
||||
tsx: ^4.21.0
|
||||
vite: ^7.3.4
|
||||
wouter: ^3.3.5
|
||||
zod: ^3.25.76
|
||||
'@replit/vite-plugin-cartographer': 0.6.1
|
||||
'@replit/vite-plugin-dev-banner': 0.1.2
|
||||
'@replit/vite-plugin-runtime-error-modal': 0.0.6
|
||||
'@tailwindcss/vite': 4.3.3
|
||||
'@tanstack/react-query': 5.101.4
|
||||
'@tanstack/react-virtual': 3.14.9
|
||||
'@types/node': 26.1.2
|
||||
'@types/react': 19.2.18
|
||||
'@types/react-dom': 19.2.4
|
||||
'@vitejs/plugin-react': 6.0.5
|
||||
class-variance-authority: 0.7.1
|
||||
clsx: 2.1.1
|
||||
drizzle-orm: 0.45.2
|
||||
framer-motion: 12.43.0
|
||||
lucide-react: 1.28.0
|
||||
react: 19.2.8
|
||||
react-dom: 19.2.8
|
||||
tailwind-merge: 3.6.0
|
||||
tailwindcss: 4.3.3
|
||||
tsx: 4.23.1
|
||||
vite: 8.2.0
|
||||
wouter: 3.10.0
|
||||
zod: 4.4.3
|
||||
|
||||
autoInstallPeers: false
|
||||
|
||||
onlyBuiltDependencies:
|
||||
- '@swc/core'
|
||||
- esbuild
|
||||
- msw
|
||||
- unrs-resolver
|
||||
# pnpm 11: allowBuilds (map) replaces onlyBuiltDependencies (list).
|
||||
# Build scripts are blocked by default (supply-chain defense). Only allow
|
||||
# scripts for packages that genuinely need a postinstall to work.
|
||||
allowBuilds:
|
||||
'@swc/core': true
|
||||
esbuild: true
|
||||
msw: true
|
||||
unrs-resolver: true
|
||||
|
||||
overrides:
|
||||
# replit uses linux-x64 only, we can exclude all other platforms
|
||||
@@ -157,7 +166,14 @@ overrides:
|
||||
"@expo/ngrok-bin>@expo/ngrok-bin-win32-ia32": "-"
|
||||
"@expo/ngrok-bin>@expo/ngrok-bin-win32-x64": "-"
|
||||
# drizzle-kit uses esbuild internally on an older version that's vulnerable, this overrides it
|
||||
"@esbuild-kit/esm-loader": "npm:tsx@^4.21.0"
|
||||
esbuild: "0.27.3"
|
||||
"@esbuild-kit/esm-loader": "npm:tsx@4.23.1"
|
||||
esbuild: "0.28.1"
|
||||
# Fix GHSA-q8mj-m7cp-5q26: qs DoS via stringify with null/undefined in comma-format arrays
|
||||
qs: ">=6.15.2"
|
||||
qs: ">=6.15.2"
|
||||
# Fix GHSA-v422-hmwv-36x6: body-parser DoS via invalid limit value (express dep)
|
||||
body-parser: ">=2.3.0"
|
||||
# Build-time tooling (orval/typedoc) advisories: force patched versions
|
||||
markdown-it: ">=14.1.2"
|
||||
linkify-it: ">=5.0.2"
|
||||
brace-expansion: ">=5.0.8"
|
||||
fast-uri: ">=3.1.4"
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:recommended",
|
||||
":maintainLockFilesWeekly",
|
||||
":semanticCommitTypeAll(chore)"
|
||||
],
|
||||
"packageRules": [
|
||||
{
|
||||
"matchDepTypes": ["dependencies", "devDependencies", "peerDependencies"],
|
||||
"matchUpdateTypes": ["patch", "minor"],
|
||||
"groupName": "non-breaking updates",
|
||||
"minimumReleaseAge": "1 day"
|
||||
},
|
||||
{
|
||||
"matchUpdateTypes": ["major"],
|
||||
"groupName": "major updates",
|
||||
"minimumReleaseAge": "7 days",
|
||||
"labels": ["major-update"],
|
||||
"prPriority": 5
|
||||
},
|
||||
{
|
||||
"matchPackageNames": ["react", "react-dom", "esbuild"],
|
||||
"enabled": false
|
||||
}
|
||||
],
|
||||
"schedule": ["on the first day of the week"],
|
||||
"lockFileMaintenance": { "enabled": true }
|
||||
}
|
||||
Reference in New Issue
Block a user