From 1679585c4caa02c53c622953fef27534ce34a947 Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Thu, 10 Apr 2025 19:56:42 +0200 Subject: [PATCH] fix: :rotating_light: Enable more Biome 2.0 rules --- app.ts | 3 +- biome.json | 58 +++++++++++++++++-- bun.lock | 9 +++ classes/database/timeline.ts | 15 ++--- classes/database/user.ts | 4 +- .../media/preprocessors/image-conversion.ts | 2 +- cli/index/rebuild.ts | 1 + cli/instance/refetch.ts | 1 + cli/user/create.ts | 1 + cli/user/delete.ts | 1 + cli/user/refetch.ts | 1 + cli/user/token.ts | 1 + entrypoints/api/index.ts | 1 + entrypoints/worker/index.ts | 1 + middlewares/boundary-check.ts | 17 +++--- packages/client/package.json | 4 +- packages/client/versia/base.ts | 8 +-- packages/client/versia/client.ts | 24 +++----- packages/plugin-kit/package.json | 2 + packages/sdk/package.json | 7 ++- routes.ts | 8 +-- 21 files changed, 116 insertions(+), 53 deletions(-) diff --git a/app.ts b/app.ts index 02951561..f1e56ecb 100644 --- a/app.ts +++ b/app.ts @@ -27,6 +27,7 @@ import { routes } from "./routes.ts"; import type { ApiRouteExports, HonoEnv } from "./types/api.ts"; // Extends Zod with OpenAPI schema generation import "zod-openapi/extend"; +import { cwd } from "node:process"; export const appFactory = async (): Promise> => { await configureLoggers(); @@ -121,7 +122,7 @@ export const appFactory = async (): Promise> => { const loader = new PluginLoader(); const plugins = await loader.loadPlugins( - join(process.cwd(), "plugins"), + join(cwd(), "plugins"), config.plugins?.autoload ?? true, config.plugins?.overrides.enabled, config.plugins?.overrides.disabled, diff --git a/biome.json b/biome.json index 52797b74..8572df4c 100644 --- a/biome.json +++ b/biome.json @@ -45,6 +45,23 @@ }, "useLiteralEnumMembers": "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", "useAsConstAssertion": "error", "useNumericLiterals": "error", @@ -67,16 +84,39 @@ "useShorthandFunctionType": "error" }, "correctness": { - "useImportExtensions": "error" + "useImportExtensions": "error", + "noConstantMathMinMaxClamp": "error", + "noUndeclaredDependencies": "error", + "noUnusedFunctionParameters": "error", + "noUnusedImports": "error", + "noUnusedPrivateClassMembers": "error", + "useArrayLiterals": "error" }, "nursery": { + "noBitwiseOperators": "error", + "noConstantBinaryExpression": "error", + "noFloatingPromises": "error", + "noGlobalDirnameFilename": "error", + "noOctalEscape": "error", + "noProcessEnv": "error", "noDuplicateElseIf": "warn", + "noProcessGlobal": "warn", + "noTsIgnore": "warn", + "useAtIndex": "warn", + "useCollapsedIf": "warn", + "useConsistentObjectDefinition": { + "level": "warn", + "options": { + "syntax": "shorthand" + } + }, "useConsistentMemberAccessibility": { "level": "warn", "options": { "accessibility": "explicit" } }, + "useParseIntRadix": "warn", "noCommonJs": "warn", "noDynamicNamespaceImportAccess": "warn", "noExportedImports": "warn", @@ -91,11 +131,21 @@ "useTrimStartEnd": "warn" }, "complexity": { - "noExcessiveCognitiveComplexity": "off" + "noForEach": "error", + "noUselessStringConcat": "error", + "useDateNow": "error", + "useSimplifiedLogicExpression": "error", + "useWhile": "error" }, "suspicious": { - "noMisplacedAssertion": "off", - "noConsole": "off" + "noDuplicateTestHooks": "error", + "noEmptyBlockStatements": "error", + "noEvolvingTypes": "error", + "noExportsInTest": "error", + "noVar": "error", + "useAwait": "error", + "useErrorMessage": "error", + "useNumberToFixedDigitsArgument": "error" } } }, diff --git a/bun.lock b/bun.lock index 82aa8dd2..3f39735e 100644 --- a/bun.lock +++ b/bun.lock @@ -93,12 +93,16 @@ "version": "0.2.0-alpha.1", "dependencies": { "@badgateway/oauth2-client": "^2.4.2", + "iso-639-1": "^3.1.5", + "zod": "^3.24.2", }, }, "packages/plugin-kit": { "name": "@versia/kit", "version": "0.0.0", "dependencies": { + "drizzle-orm": "^0.41.0", + "hono": "^4.7.6", "mitt": "^3.0.1", "zod": "^3.23.8", "zod-to-json-schema": "^3.23.3", @@ -108,6 +112,11 @@ "packages/sdk": { "name": "@versia/sdk", "version": "0.0.1", + "dependencies": { + "magic-regexp": "^0.9.0", + "mime-types": "^3.0.1", + "zod": "^3.24.2", + }, }, }, "trustedDependencies": [ diff --git a/classes/database/timeline.ts b/classes/database/timeline.ts index 1d885378..3d529b1a 100644 --- a/classes/database/timeline.ts +++ b/classes/database/timeline.ts @@ -141,11 +141,11 @@ export class Timeline { if (notes.length >= (limit ?? 20)) { const objectAfter = await Note.fromSql( - gt(Notes.id, notes[notes.length - 1].data.id), + gt(Notes.id, notes.at(-1)?.data.id ?? ""), ); if (objectAfter) { 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 { if (users.length >= (limit ?? 20)) { const objectAfter = await User.fromSql( - gt(Users.id, users[users.length - 1].id), + gt(Users.id, users.at(-1)?.id ?? ""), ); if (objectAfter) { 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 { if (notifications.length >= (limit ?? 20)) { const objectAfter = await Notification.fromSql( - gt( - Notifications.id, - notifications[notifications.length - 1].data.id, - ), + gt(Notifications.id, notifications.at(-1)?.data.id ?? ""), ); if (objectAfter) { 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"`, ); } } diff --git a/classes/database/user.ts b/classes/database/user.ts index 6640da50..50d6c037 100644 --- a/classes/database/user.ts +++ b/classes/database/user.ts @@ -499,7 +499,7 @@ export class User extends BaseInterface { await note.author.notify("favourite", this, note); } else if (this.local && note.author.remote) { // Federate the like - this.federateToFollowers(newLike.toVersia()); + await this.federateToFollowers(newLike.toVersia()); } return newLike; @@ -528,7 +528,7 @@ export class User extends BaseInterface { await likeToDelete.clearRelatedNotifications(); } else if (this.local && note.author.remote) { // User is local, federate the delete - this.federateToFollowers(likeToDelete.unlikeToVersia(this)); + await this.federateToFollowers(likeToDelete.unlikeToVersia(this)); } } diff --git a/classes/media/preprocessors/image-conversion.ts b/classes/media/preprocessors/image-conversion.ts index ae5063ef..853a7135 100644 --- a/classes/media/preprocessors/image-conversion.ts +++ b/classes/media/preprocessors/image-conversion.ts @@ -52,7 +52,7 @@ const isConvertible = ( */ const extractFilenameFromPath = (path: string): string => { const pathParts = path.split(/(? { // Checks that FormData boundary is present const contentType = context.req.header("content-type"); - if (contentType?.includes("multipart/form-data")) { - if (!contentType.includes("boundary")) { - throw new ApiError( - 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", - ); - } + if ( + contentType?.includes("multipart/form-data") && + !contentType.includes("boundary") + ) { + throw new ApiError( + 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(); diff --git a/packages/client/package.json b/packages/client/package.json index b1104dfe..cd615d24 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -63,6 +63,8 @@ ], "packageManager": "bun@1.2.5", "dependencies": { - "@badgateway/oauth2-client": "^2.4.2" + "@badgateway/oauth2-client": "^2.4.2", + "iso-639-1": "^3.1.5", + "zod": "^3.24.2" } } diff --git a/packages/client/versia/base.ts b/packages/client/versia/base.ts index c4b31032..b648f0e0 100644 --- a/packages/client/versia/base.ts +++ b/packages/client/versia/base.ts @@ -151,11 +151,9 @@ export class BaseClient { if (this.accessToken) { headers.set("Authorization", `Bearer ${this.accessToken}`); } - if (body) { - if (!(body instanceof FormData)) { - headers.set("Content-Type", "application/json; charset=utf-8"); - } // else: let FormData set the content type, as it knows best (boundary, etc.) - } + if (body && !(body instanceof FormData)) { + headers.set("Content-Type", "application/json; charset=utf-8"); + } // else: let FormData set the content type, as it knows best (boundary, etc.) for (const [key, value] of Object.entries(extra?.headers || {})) { headers.set(key, value); diff --git a/packages/client/versia/client.ts b/packages/client/versia/client.ts index a1881c84..bc134320 100644 --- a/packages/client/versia/client.ts +++ b/packages/client/versia/client.ts @@ -1229,10 +1229,8 @@ export class Client extends BaseClient { ): Promise[]>> { const params = new URLSearchParams(); - if (options) { - if (options.limit) { - params.set("limit", options.limit.toString()); - } + if (options?.limit) { + params.set("limit", options.limit.toString()); } return this.get[]>( @@ -1449,10 +1447,8 @@ export class Client extends BaseClient { ): Promise[]>> { const params = new URLSearchParams(); - if (options) { - if (options.limit) { - params.set("limit", options.limit.toString()); - } + if (options?.limit) { + params.set("limit", options.limit.toString()); } return this.get[]>(`/api/v1/trends?${params}`); @@ -1839,10 +1835,8 @@ export class Client extends BaseClient { params.append("id[]", id); } - if (options) { - if (options.with_suspended) { - params.set("with_suspended", "true"); - } + if (options?.with_suspended) { + params.set("with_suspended", "true"); } return this.get[]>( @@ -2121,10 +2115,8 @@ export class Client extends BaseClient { ): Promise[]>> { const params = new URLSearchParams(); - if (options) { - if (options.limit) { - params.set("limit", options.limit.toString()); - } + if (options?.limit) { + params.set("limit", options.limit.toString()); } return this.get[]>( diff --git a/packages/plugin-kit/package.json b/packages/plugin-kit/package.json index 1d7e0f5b..41c40030 100644 --- a/packages/plugin-kit/package.json +++ b/packages/plugin-kit/package.json @@ -32,6 +32,8 @@ }, "private": true, "dependencies": { + "drizzle-orm": "^0.41.0", + "hono": "^4.7.6", "mitt": "^3.0.1", "zod": "^3.23.8", "zod-to-json-schema": "^3.23.3", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 2220b9de..a6b10fb8 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -71,5 +71,10 @@ "typescript", "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" + } } diff --git a/routes.ts b/routes.ts index f3a225a9..ed023400 100644 --- a/routes.ts +++ b/routes.ts @@ -1,17 +1,15 @@ import { join } from "node:path"; +import { cwd } from "node:process"; import { FileSystemRouter } from "bun"; // Returns the route filesystem path when given a URL export const routeMatcher = new FileSystemRouter({ style: "nextjs", - dir: `${process.cwd()}/api`, + dir: `${cwd()}/api`, fileExtensions: [".ts", ".js"], }); export const routes = Object.fromEntries( Object.entries(routeMatcher.routes) .filter(([route]) => !route.endsWith(".test")) - .map(([route, path]) => [ - route, - path.replace(join(process.cwd()), "."), - ]), + .map(([route, path]) => [route, path.replace(join(cwd()), ".")]), ) as Record;