fix: 🚨 Enable more Biome 2.0 rules
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 4s
Build Docker Images / lint (push) Failing after 10s
Build Docker Images / check (push) Failing after 10s
Build Docker Images / tests (push) Failing after 6s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s

This commit is contained in:
Jesse Wierzbinski 2025-04-10 19:56:42 +02:00
parent 963173cdae
commit 1679585c4c
No known key found for this signature in database
21 changed files with 116 additions and 53 deletions

3
app.ts
View file

@ -27,6 +27,7 @@ import { routes } from "./routes.ts";
import type { ApiRouteExports, HonoEnv } from "./types/api.ts"; import type { ApiRouteExports, HonoEnv } from "./types/api.ts";
// Extends Zod with OpenAPI schema generation // Extends Zod with OpenAPI schema generation
import "zod-openapi/extend"; import "zod-openapi/extend";
import { cwd } from "node:process";
export const appFactory = async (): Promise<Hono<HonoEnv>> => { export const appFactory = async (): Promise<Hono<HonoEnv>> => {
await configureLoggers(); await configureLoggers();
@ -121,7 +122,7 @@ export const appFactory = async (): Promise<Hono<HonoEnv>> => {
const loader = new PluginLoader(); const loader = new PluginLoader();
const plugins = await loader.loadPlugins( const plugins = await loader.loadPlugins(
join(process.cwd(), "plugins"), join(cwd(), "plugins"),
config.plugins?.autoload ?? true, config.plugins?.autoload ?? true,
config.plugins?.overrides.enabled, config.plugins?.overrides.enabled,
config.plugins?.overrides.disabled, config.plugins?.overrides.disabled,

View file

@ -45,6 +45,23 @@
}, },
"useLiteralEnumMembers": "error", "useLiteralEnumMembers": "error",
"noCommaOperator": "error", "noCommaOperator": "error",
"noNegationElse": "error",
"noYodaExpression": "error",
"useBlockStatements": "error",
"useCollapsedElseIf": "error",
"useConsistentArrayType": {
"level": "error",
"options": {
"syntax": "shorthand"
}
},
"useConsistentBuiltinInstantiation": "error",
"useExplicitLengthCheck": "error",
"useForOf": "error",
"useNodeAssertStrict": "error",
"useShorthandAssign": "error",
"useThrowNewError": "error",
"useThrowOnlyError": "error",
"useNodejsImportProtocol": "error", "useNodejsImportProtocol": "error",
"useAsConstAssertion": "error", "useAsConstAssertion": "error",
"useNumericLiterals": "error", "useNumericLiterals": "error",
@ -67,16 +84,39 @@
"useShorthandFunctionType": "error" "useShorthandFunctionType": "error"
}, },
"correctness": { "correctness": {
"useImportExtensions": "error" "useImportExtensions": "error",
"noConstantMathMinMaxClamp": "error",
"noUndeclaredDependencies": "error",
"noUnusedFunctionParameters": "error",
"noUnusedImports": "error",
"noUnusedPrivateClassMembers": "error",
"useArrayLiterals": "error"
}, },
"nursery": { "nursery": {
"noBitwiseOperators": "error",
"noConstantBinaryExpression": "error",
"noFloatingPromises": "error",
"noGlobalDirnameFilename": "error",
"noOctalEscape": "error",
"noProcessEnv": "error",
"noDuplicateElseIf": "warn", "noDuplicateElseIf": "warn",
"noProcessGlobal": "warn",
"noTsIgnore": "warn",
"useAtIndex": "warn",
"useCollapsedIf": "warn",
"useConsistentObjectDefinition": {
"level": "warn",
"options": {
"syntax": "shorthand"
}
},
"useConsistentMemberAccessibility": { "useConsistentMemberAccessibility": {
"level": "warn", "level": "warn",
"options": { "options": {
"accessibility": "explicit" "accessibility": "explicit"
} }
}, },
"useParseIntRadix": "warn",
"noCommonJs": "warn", "noCommonJs": "warn",
"noDynamicNamespaceImportAccess": "warn", "noDynamicNamespaceImportAccess": "warn",
"noExportedImports": "warn", "noExportedImports": "warn",
@ -91,11 +131,21 @@
"useTrimStartEnd": "warn" "useTrimStartEnd": "warn"
}, },
"complexity": { "complexity": {
"noExcessiveCognitiveComplexity": "off" "noForEach": "error",
"noUselessStringConcat": "error",
"useDateNow": "error",
"useSimplifiedLogicExpression": "error",
"useWhile": "error"
}, },
"suspicious": { "suspicious": {
"noMisplacedAssertion": "off", "noDuplicateTestHooks": "error",
"noConsole": "off" "noEmptyBlockStatements": "error",
"noEvolvingTypes": "error",
"noExportsInTest": "error",
"noVar": "error",
"useAwait": "error",
"useErrorMessage": "error",
"useNumberToFixedDigitsArgument": "error"
} }
} }
}, },

View file

@ -93,12 +93,16 @@
"version": "0.2.0-alpha.1", "version": "0.2.0-alpha.1",
"dependencies": { "dependencies": {
"@badgateway/oauth2-client": "^2.4.2", "@badgateway/oauth2-client": "^2.4.2",
"iso-639-1": "^3.1.5",
"zod": "^3.24.2",
}, },
}, },
"packages/plugin-kit": { "packages/plugin-kit": {
"name": "@versia/kit", "name": "@versia/kit",
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"drizzle-orm": "^0.41.0",
"hono": "^4.7.6",
"mitt": "^3.0.1", "mitt": "^3.0.1",
"zod": "^3.23.8", "zod": "^3.23.8",
"zod-to-json-schema": "^3.23.3", "zod-to-json-schema": "^3.23.3",
@ -108,6 +112,11 @@
"packages/sdk": { "packages/sdk": {
"name": "@versia/sdk", "name": "@versia/sdk",
"version": "0.0.1", "version": "0.0.1",
"dependencies": {
"magic-regexp": "^0.9.0",
"mime-types": "^3.0.1",
"zod": "^3.24.2",
},
}, },
}, },
"trustedDependencies": [ "trustedDependencies": [

View file

@ -141,11 +141,11 @@ export class Timeline<Type extends Note | User | Notification> {
if (notes.length >= (limit ?? 20)) { if (notes.length >= (limit ?? 20)) {
const objectAfter = await Note.fromSql( const objectAfter = await Note.fromSql(
gt(Notes.id, notes[notes.length - 1].data.id), gt(Notes.id, notes.at(-1)?.data.id ?? ""),
); );
if (objectAfter) { if (objectAfter) {
linkHeader.push( linkHeader.push(
`<${urlWithoutQuery}?limit=${limit ?? 20}&max_id=${notes[notes.length - 1].data.id}>; rel="next"`, `<${urlWithoutQuery}?limit=${limit ?? 20}&max_id=${notes.at(-1)?.data.id}>; rel="next"`,
); );
} }
} }
@ -169,11 +169,11 @@ export class Timeline<Type extends Note | User | Notification> {
if (users.length >= (limit ?? 20)) { if (users.length >= (limit ?? 20)) {
const objectAfter = await User.fromSql( const objectAfter = await User.fromSql(
gt(Users.id, users[users.length - 1].id), gt(Users.id, users.at(-1)?.id ?? ""),
); );
if (objectAfter) { if (objectAfter) {
linkHeader.push( linkHeader.push(
`<${urlWithoutQuery}?limit=${limit ?? 20}&max_id=${users[users.length - 1].id}>; rel="next"`, `<${urlWithoutQuery}?limit=${limit ?? 20}&max_id=${users.at(-1)?.id}>; rel="next"`,
); );
} }
} }
@ -199,14 +199,11 @@ export class Timeline<Type extends Note | User | Notification> {
if (notifications.length >= (limit ?? 20)) { if (notifications.length >= (limit ?? 20)) {
const objectAfter = await Notification.fromSql( const objectAfter = await Notification.fromSql(
gt( gt(Notifications.id, notifications.at(-1)?.data.id ?? ""),
Notifications.id,
notifications[notifications.length - 1].data.id,
),
); );
if (objectAfter) { if (objectAfter) {
linkHeader.push( linkHeader.push(
`<${urlWithoutQuery}?limit=${limit ?? 20}&max_id=${notifications[notifications.length - 1].data.id}>; rel="next"`, `<${urlWithoutQuery}?limit=${limit ?? 20}&max_id=${notifications.at(-1)?.data.id}>; rel="next"`,
); );
} }
} }

View file

@ -499,7 +499,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
await note.author.notify("favourite", this, note); await note.author.notify("favourite", this, note);
} else if (this.local && note.author.remote) { } else if (this.local && note.author.remote) {
// Federate the like // Federate the like
this.federateToFollowers(newLike.toVersia()); await this.federateToFollowers(newLike.toVersia());
} }
return newLike; return newLike;
@ -528,7 +528,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
await likeToDelete.clearRelatedNotifications(); await likeToDelete.clearRelatedNotifications();
} else if (this.local && note.author.remote) { } else if (this.local && note.author.remote) {
// User is local, federate the delete // User is local, federate the delete
this.federateToFollowers(likeToDelete.unlikeToVersia(this)); await this.federateToFollowers(likeToDelete.unlikeToVersia(this));
} }
} }

View file

@ -52,7 +52,7 @@ const isConvertible = (
*/ */
const extractFilenameFromPath = (path: string): string => { const extractFilenameFromPath = (path: string): string => {
const pathParts = path.split(/(?<!\\)\//); const pathParts = path.split(/(?<!\\)\//);
return pathParts[pathParts.length - 1]; return pathParts.at(-1) as string;
}; };
/** /**

View file

@ -1,4 +1,5 @@
// @ts-expect-error - Root import is required or the Clec type definitions won't work // @ts-expect-error - Root import is required or the Clec type definitions won't work
// biome-ignore lint/correctness/noUnusedImports: Root import is required or the Clec type definitions won't work
import { defineCommand, type Root } from "clerc"; import { defineCommand, type Root } from "clerc";
import ora from "ora"; import ora from "ora";
import { import {

View file

@ -1,5 +1,6 @@
import chalk from "chalk"; import chalk from "chalk";
// @ts-expect-error - Root import is required or the Clec type definitions won't work // @ts-expect-error - Root import is required or the Clec type definitions won't work
// biome-ignore lint/correctness/noUnusedImports: Root import is required or the Clec type definitions won't work
import { defineCommand, type Root } from "clerc"; import { defineCommand, type Root } from "clerc";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import { Instance } from "~/classes/database/instance.ts"; import { Instance } from "~/classes/database/instance.ts";

View file

@ -1,5 +1,6 @@
import chalk from "chalk"; import chalk from "chalk";
// @ts-expect-error - Root import is required or the Clec type definitions won't work // @ts-expect-error - Root import is required or the Clec type definitions won't work
// biome-ignore lint/correctness/noUnusedImports: Root import is required or the Clec type definitions won't work
import { defineCommand, type Root } from "clerc"; import { defineCommand, type Root } from "clerc";
import { and, eq, isNull } from "drizzle-orm"; import { and, eq, isNull } from "drizzle-orm";
import { renderUnicodeCompact } from "uqr"; import { renderUnicodeCompact } from "uqr";

View file

@ -1,6 +1,7 @@
import confirm from "@inquirer/confirm"; import confirm from "@inquirer/confirm";
import chalk from "chalk"; import chalk from "chalk";
// @ts-expect-error - Root import is required or the Clec type definitions won't work // @ts-expect-error - Root import is required or the Clec type definitions won't work
// biome-ignore lint/correctness/noUnusedImports: Root import is required or the Clec type definitions won't work
import { defineCommand, type Root } from "clerc"; import { defineCommand, type Root } from "clerc";
import { retrieveUser } from "../utils.ts"; import { retrieveUser } from "../utils.ts";

View file

@ -1,5 +1,6 @@
import chalk from "chalk"; import chalk from "chalk";
// @ts-expect-error - Root import is required or the Clec type definitions won't work // @ts-expect-error - Root import is required or the Clec type definitions won't work
// biome-ignore lint/correctness/noUnusedImports: Root import is required or the Clec type definitions won't work
import { defineCommand, type Root } from "clerc"; import { defineCommand, type Root } from "clerc";
import ora from "ora"; import ora from "ora";
import { User } from "~/classes/database/user.ts"; import { User } from "~/classes/database/user.ts";

View file

@ -1,6 +1,7 @@
import { randomUUIDv7 } from "bun"; import { randomUUIDv7 } from "bun";
import chalk from "chalk"; import chalk from "chalk";
// @ts-expect-error - Root import is required or the Clec type definitions won't work // @ts-expect-error - Root import is required or the Clec type definitions won't work
// biome-ignore lint/correctness/noUnusedImports: Root import is required or the Clec type definitions won't work
import { defineCommand, type Root } from "clerc"; import { defineCommand, type Root } from "clerc";
import { randomString } from "@/math.ts"; import { randomString } from "@/math.ts";
import { Token } from "~/classes/database/token.ts"; import { Token } from "~/classes/database/token.ts";

View file

@ -1,4 +1,5 @@
import cluster from "node:cluster"; import cluster from "node:cluster";
import process from "node:process";
import { Youch } from "youch"; import { Youch } from "youch";
import { sentry } from "@/sentry"; import { sentry } from "@/sentry";
import { createServer } from "@/server"; import { createServer } from "@/server";

View file

@ -1,3 +1,4 @@
import process from "node:process";
import { getLogger } from "@logtape/logtape"; import { getLogger } from "@logtape/logtape";
import chalk from "chalk"; import chalk from "chalk";
import { sentry } from "@/sentry"; import { sentry } from "@/sentry";

View file

@ -5,14 +5,15 @@ export const boundaryCheck = createMiddleware(async (context, next) => {
// Checks that FormData boundary is present // Checks that FormData boundary is present
const contentType = context.req.header("content-type"); const contentType = context.req.header("content-type");
if (contentType?.includes("multipart/form-data")) { if (
if (!contentType.includes("boundary")) { contentType?.includes("multipart/form-data") &&
throw new ApiError( !contentType.includes("boundary")
400, ) {
"Missing FormData boundary", throw new ApiError(
"You are sending a request with a multipart/form-data content type but without a boundary. Please include a boundary in the Content-Type header. For more information, visit https://stackoverflow.com/questions/3508338/what-is-the-boundary-in-multipart-form-data", 400,
); "Missing FormData boundary",
} "You are sending a request with a multipart/form-data content type but without a boundary. Please include a boundary in the Content-Type header. For more information, visit https://stackoverflow.com/questions/3508338/what-is-the-boundary-in-multipart-form-data",
);
} }
await next(); await next();

View file

@ -63,6 +63,8 @@
], ],
"packageManager": "bun@1.2.5", "packageManager": "bun@1.2.5",
"dependencies": { "dependencies": {
"@badgateway/oauth2-client": "^2.4.2" "@badgateway/oauth2-client": "^2.4.2",
"iso-639-1": "^3.1.5",
"zod": "^3.24.2"
} }
} }

View file

@ -151,11 +151,9 @@ export class BaseClient {
if (this.accessToken) { if (this.accessToken) {
headers.set("Authorization", `Bearer ${this.accessToken}`); headers.set("Authorization", `Bearer ${this.accessToken}`);
} }
if (body) { if (body && !(body instanceof FormData)) {
if (!(body instanceof FormData)) { headers.set("Content-Type", "application/json; charset=utf-8");
headers.set("Content-Type", "application/json; charset=utf-8"); } // else: let FormData set the content type, as it knows best (boundary, etc.)
} // else: let FormData set the content type, as it knows best (boundary, etc.)
}
for (const [key, value] of Object.entries(extra?.headers || {})) { for (const [key, value] of Object.entries(extra?.headers || {})) {
headers.set(key, value); headers.set(key, value);

View file

@ -1229,10 +1229,8 @@ export class Client extends BaseClient {
): Promise<Output<z.infer<typeof Account>[]>> { ): Promise<Output<z.infer<typeof Account>[]>> {
const params = new URLSearchParams(); const params = new URLSearchParams();
if (options) { if (options?.limit) {
if (options.limit) { params.set("limit", options.limit.toString());
params.set("limit", options.limit.toString());
}
} }
return this.get<z.infer<typeof Account>[]>( return this.get<z.infer<typeof Account>[]>(
@ -1449,10 +1447,8 @@ export class Client extends BaseClient {
): Promise<Output<z.infer<typeof Tag>[]>> { ): Promise<Output<z.infer<typeof Tag>[]>> {
const params = new URLSearchParams(); const params = new URLSearchParams();
if (options) { if (options?.limit) {
if (options.limit) { params.set("limit", options.limit.toString());
params.set("limit", options.limit.toString());
}
} }
return this.get<z.infer<typeof Tag>[]>(`/api/v1/trends?${params}`); return this.get<z.infer<typeof Tag>[]>(`/api/v1/trends?${params}`);
@ -1839,10 +1835,8 @@ export class Client extends BaseClient {
params.append("id[]", id); params.append("id[]", id);
} }
if (options) { if (options?.with_suspended) {
if (options.with_suspended) { params.set("with_suspended", "true");
params.set("with_suspended", "true");
}
} }
return this.get<z.infer<typeof Relationship>[]>( return this.get<z.infer<typeof Relationship>[]>(
@ -2121,10 +2115,8 @@ export class Client extends BaseClient {
): Promise<Output<z.infer<typeof Account>[]>> { ): Promise<Output<z.infer<typeof Account>[]>> {
const params = new URLSearchParams(); const params = new URLSearchParams();
if (options) { if (options?.limit) {
if (options.limit) { params.set("limit", options.limit.toString());
params.set("limit", options.limit.toString());
}
} }
return this.get<z.infer<typeof Account>[]>( return this.get<z.infer<typeof Account>[]>(

View file

@ -32,6 +32,8 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"drizzle-orm": "^0.41.0",
"hono": "^4.7.6",
"mitt": "^3.0.1", "mitt": "^3.0.1",
"zod": "^3.23.8", "zod": "^3.23.8",
"zod-to-json-schema": "^3.23.3", "zod-to-json-schema": "^3.23.3",

View file

@ -71,5 +71,10 @@
"typescript", "typescript",
"sdk" "sdk"
], ],
"packageManager": "bun@1.2.5" "packageManager": "bun@1.2.5",
"dependencies": {
"magic-regexp": "^0.9.0",
"mime-types": "^3.0.1",
"zod": "^3.24.2"
}
} }

View file

@ -1,17 +1,15 @@
import { join } from "node:path"; import { join } from "node:path";
import { cwd } from "node:process";
import { FileSystemRouter } from "bun"; import { FileSystemRouter } from "bun";
// Returns the route filesystem path when given a URL // Returns the route filesystem path when given a URL
export const routeMatcher = new FileSystemRouter({ export const routeMatcher = new FileSystemRouter({
style: "nextjs", style: "nextjs",
dir: `${process.cwd()}/api`, dir: `${cwd()}/api`,
fileExtensions: [".ts", ".js"], fileExtensions: [".ts", ".js"],
}); });
export const routes = Object.fromEntries( export const routes = Object.fromEntries(
Object.entries(routeMatcher.routes) Object.entries(routeMatcher.routes)
.filter(([route]) => !route.endsWith(".test")) .filter(([route]) => !route.endsWith(".test"))
.map(([route, path]) => [ .map(([route, path]) => [route, path.replace(join(cwd()), ".")]),
route,
path.replace(join(process.cwd()), "."),
]),
) as Record<string, string>; ) as Record<string, string>;