diff --git a/biome.json b/biome.json index 747c52c..0b8b793 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/1.7.3/schema.json", + "$schema": "https://biomejs.dev/schemas/1.8.1/schema.json", "organizeImports": { "enabled": true, "ignore": ["node_modules", "dist"] @@ -7,7 +7,68 @@ "linter": { "enabled": true, "rules": { - "recommended": true + "all": true, + "correctness": { + "noNodejsModules": "off" + }, + "complexity": { + "noExcessiveCognitiveComplexity": "off" + }, + "style": { + "noDefaultExport": "off", + "noParameterProperties": "off", + "noNamespaceImport": "off", + "useFilenamingConvention": "off", + "useNamingConvention": { + "level": "warn", + "options": { + "requireAscii": false, + "strictCase": false, + "conventions": [ + { + "selector": { + "kind": "typeProperty" + }, + "formats": [ + "camelCase", + "CONSTANT_CASE", + "PascalCase", + "snake_case" + ] + }, + { + "selector": { + "kind": "objectLiteralProperty", + "scope": "any" + }, + "formats": [ + "camelCase", + "CONSTANT_CASE", + "PascalCase", + "snake_case" + ] + }, + { + "selector": { + "kind": "classMethod", + "scope": "any" + }, + "formats": ["camelCase", "PascalCase"] + } + ] + } + } + }, + "nursery": { + "noDuplicateElseIf": "warn", + "noDuplicateJsonKeys": "warn", + "noEvolvingTypes": "warn", + "noYodaExpression": "warn", + "useConsistentBuiltinInstantiation": "warn", + "useErrorMessage": "warn", + "useImportExtensions": "off", + "useThrowNewError": "warn" + } }, "ignore": ["node_modules", "dist"] }, @@ -16,5 +77,8 @@ "indentStyle": "space", "indentWidth": 4, "ignore": ["node_modules", "dist"] + }, + "javascript": { + "globals": ["Bun"] } } diff --git a/client/lysand/base.ts b/client/lysand/base.ts index 8441b77..3750af2 100644 --- a/client/lysand/base.ts +++ b/client/lysand/base.ts @@ -24,7 +24,9 @@ export interface Output { const objectToFormData = (obj: ConvertibleObject): FormData => { return Object.keys(obj).reduce((formData, key) => { - if (obj[key] === undefined || obj[key] === null) return formData; + if (obj[key] === undefined || obj[key] === null) { + return formData; + } if (obj[key] instanceof File) { formData.append(key, obj[key] as Blob); return formData; @@ -113,12 +115,12 @@ export class BaseClient { }; } - private async constructRequest( + private constructRequest( path: string, method: HttpVerb, body?: object | FormData, extra?: RequestInit, - ): Promise { + ): Request { const headers = new Headers({ "User-Agent": DEFAULT_UA, }); @@ -148,32 +150,30 @@ export class BaseClient { }); } - public async get( + public get( path: string, extra?: RequestInit, ): Promise> { - return await this.request( - await this.constructRequest(path, "GET", undefined, extra), + return this.request( + this.constructRequest(path, "GET", undefined, extra), ); } - public async post( + public post( path: string, body?: object, extra?: RequestInit, ): Promise> { - return await this.request( - await this.constructRequest(path, "POST", body, extra), - ); + return this.request(this.constructRequest(path, "POST", body, extra)); } - public async postForm( + public postForm( path: string, body: FormData | ConvertibleObject, extra?: RequestInit, ): Promise> { - return await this.request( - await this.constructRequest( + return this.request( + this.constructRequest( path, "POST", body instanceof FormData ? body : objectToFormData(body), @@ -182,23 +182,21 @@ export class BaseClient { ); } - public async put( + public put( path: string, body?: object, extra?: RequestInit, ): Promise> { - return await this.request( - await this.constructRequest(path, "PUT", body, extra), - ); + return this.request(this.constructRequest(path, "PUT", body, extra)); } - public async putForm( + public putForm( path: string, body: FormData | ConvertibleObject, extra?: RequestInit, ): Promise> { - return await this.request( - await this.constructRequest( + return this.request( + this.constructRequest( path, "PUT", body instanceof FormData ? body : objectToFormData(body), @@ -207,23 +205,21 @@ export class BaseClient { ); } - public async patch( + public patch( path: string, body?: object, extra?: RequestInit, ): Promise> { - return await this.request( - await this.constructRequest(path, "PATCH", body, extra), - ); + return this.request(this.constructRequest(path, "PATCH", body, extra)); } - public async patchForm( + public patchForm( path: string, body: FormData | ConvertibleObject, extra?: RequestInit, ): Promise> { - return await this.request( - await this.constructRequest( + return this.request( + this.constructRequest( path, "PATCH", body instanceof FormData ? body : objectToFormData(body), @@ -232,23 +228,21 @@ export class BaseClient { ); } - public async delete( + public delete( path: string, body?: object, extra?: RequestInit, ): Promise> { - return await this.request( - await this.constructRequest(path, "DELETE", body, extra), - ); + return this.request(this.constructRequest(path, "DELETE", body, extra)); } - public async deleteForm( + public deleteForm( path: string, body: FormData | ConvertibleObject, extra?: RequestInit, ): Promise> { - return await this.request( - await this.constructRequest( + return this.request( + this.constructRequest( path, "DELETE", body instanceof FormData ? body : objectToFormData(body), diff --git a/client/lysand/lysand.ts b/client/lysand/lysand.ts index b37a48b..944cce1 100644 --- a/client/lysand/lysand.ts +++ b/client/lysand/lysand.ts @@ -71,12 +71,12 @@ export class LysandClient extends BaseClient { */ public addAccountToList( id: string, - account_ids: string[], + accountIds: string[], extra?: RequestInit, ): Promise> { return this.post( `/api/v1/lists/${id}/accounts`, - { account_ids }, + { accountIds }, extra, ); } @@ -175,7 +175,7 @@ export class LysandClient extends BaseClient { * @param options.website URL to the application's website. */ public createApp( - client_name: string, + clientName: string, options?: Partial<{ redirect_uris: string; scopes: string[]; @@ -183,7 +183,7 @@ export class LysandClient extends BaseClient { }>, ): Promise> { return this.postForm("/api/v1/apps", { - client_name, + clientName, ...options, scopes: options?.scopes?.join(" ") || DEFAULT_SCOPE.join(" "), redirect_uris: options?.redirect_uris || NO_REDIRECT, @@ -243,12 +243,12 @@ export class LysandClient extends BaseClient { */ public deleteAccountsFromList( id: string, - account_ids: string[], + accountIds: string[], extra?: RequestInit, ): Promise> { return this.delete( `/api/v1/lists/${id}/accounts`, - { account_ids }, + { accountIds }, extra, ); } @@ -441,19 +441,19 @@ export class LysandClient extends BaseClient { * @param redirect_uri Must be the same URI as the time when you register your OAuth2 application */ public fetchAccessToken( - client_id: string, - client_secret: string, + clientId: string, + clientSecret: string, code?: string, - redirect_uri: string = NO_REDIRECT, + redirectUri: string = NO_REDIRECT, extra?: RequestInit, ): Promise> { return this.postForm( "/oauth/token", { - client_id, - client_secret, + clientId, + clientSecret, code, - redirect_uri, + redirectUri, grant_type: "authorization_code", }, extra, @@ -505,8 +505,8 @@ export class LysandClient extends BaseClient { * @returns */ public generateAuthUrl( - client_id: string, - client_secret: string, + clientId: string, + clientSecret: string, options: Partial<{ redirect_uri: string; scopes: string[]; @@ -514,8 +514,8 @@ export class LysandClient extends BaseClient { ): Promise { const oauthClient = new OAuth2Client({ server: this.baseUrl.toString(), - clientId: client_id, - clientSecret: client_secret, + clientId: clientId, + clientSecret: clientSecret, tokenEndpoint: "/oauth/token", authorizationEndpoint: "/oauth/authorize", }); @@ -560,9 +560,15 @@ export class LysandClient extends BaseClient { const params = new URLSearchParams(); if (options) { - if (options.max_id) params.set("max_id", options.max_id); - if (options.since_id) params.set("since_id", options.since_id); - if (options.limit) params.set("limit", options.limit.toString()); + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } } return this.get( @@ -592,9 +598,15 @@ export class LysandClient extends BaseClient { const params = new URLSearchParams(); if (options) { - if (options.max_id) params.set("max_id", options.max_id); - if (options.since_id) params.set("since_id", options.since_id); - if (options.limit) params.set("limit", options.limit.toString()); + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } } return this.get( @@ -647,14 +659,30 @@ export class LysandClient extends BaseClient { const params = new URLSearchParams(); if (options) { - if (options.max_id) params.set("max_id", options.max_id); - if (options.min_id) params.set("min_id", options.min_id); - if (options.since_id) params.set("since_id", options.since_id); - if (options.limit) params.set("limit", options.limit.toString()); - if (options.only_media) params.set("only_media", "true"); - if (options.pinned) params.set("pinned", "true"); - if (options.exclude_replies) params.set("exclude_replies", "true"); - if (options.exclude_reblogs) params.set("exclude_reblogs", "true"); + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + if (options.only_media) { + params.set("only_media", "true"); + } + if (options.pinned) { + params.set("pinned", "true"); + } + if (options.exclude_replies) { + params.set("exclude_replies", "true"); + } + if (options.exclude_reblogs) { + params.set("exclude_reblogs", "true"); + } } return this.get( @@ -686,10 +714,18 @@ export class LysandClient extends BaseClient { const params = new URLSearchParams(); if (options) { - if (options.max_id) params.set("max_id", options.max_id); - if (options.since_id) params.set("since_id", options.since_id); - if (options.min_id) params.set("min_id", options.min_id); - if (options.limit) params.set("limit", options.limit.toString()); + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } } return this.get( @@ -716,9 +752,15 @@ export class LysandClient extends BaseClient { const params = new URLSearchParams(); if (options) { - if (options.max_id) params.set("max_id", options.max_id); - if (options.min_id) params.set("min_id", options.min_id); - if (options.limit) params.set("limit", options.limit.toString()); + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } } return this.get(`/api/v1/blocks?${params}`); @@ -744,10 +786,18 @@ export class LysandClient extends BaseClient { const params = new URLSearchParams(); if (options) { - if (options.max_id) params.set("max_id", options.max_id); - if (options.min_id) params.set("min_id", options.min_id); - if (options.since_id) params.set("since_id", options.since_id); - if (options.limit) params.set("limit", options.limit.toString()); + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } } return this.get(`/api/v1/bookmarks?${params}`); @@ -775,10 +825,18 @@ export class LysandClient extends BaseClient { const params = new URLSearchParams(); if (options) { - if (options.max_id) params.set("max_id", options.max_id); - if (options.min_id) params.set("min_id", options.min_id); - if (options.since_id) params.set("since_id", options.since_id); - if (options.limit) params.set("limit", options.limit.toString()); + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } } return this.get( @@ -805,9 +863,15 @@ export class LysandClient extends BaseClient { const params = new URLSearchParams(); if (options) { - if (options.max_id) params.set("max_id", options.max_id); - if (options.since_id) params.set("since_id", options.since_id); - if (options.limit) params.set("limit", options.limit.toString()); + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } } return this.get(`/api/v1/domain_blocks?${params}`); @@ -832,9 +896,15 @@ export class LysandClient extends BaseClient { const params = new URLSearchParams(); if (options) { - if (options.max_id) params.set("max_id", options.max_id); - if (options.since_id) params.set("since_id", options.since_id); - if (options.limit) params.set("limit", options.limit.toString()); + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } } return this.get(`/api/v1/endorsements?${params}`, extra); @@ -858,9 +928,15 @@ export class LysandClient extends BaseClient { const params = new URLSearchParams(); if (options) { - if (options.max_id) params.set("max_id", options.max_id); - if (options.min_id) params.set("min_id", options.min_id); - if (options.limit) params.set("limit", options.limit.toString()); + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } } return this.get(`/api/v1/favourites?${params}`); @@ -894,7 +970,9 @@ export class LysandClient extends BaseClient { 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/follow_requests?${params}`); @@ -932,11 +1010,21 @@ export class LysandClient extends BaseClient { const params = new URLSearchParams(); if (options) { - if (options.max_id) params.set("max_id", options.max_id); - if (options.min_id) params.set("min_id", options.min_id); - if (options.since_id) params.set("since_id", options.since_id); - if (options.limit) params.set("limit", options.limit.toString()); - if (options.local) params.set("local", "true"); + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + if (options.local) { + params.set("local", "true"); + } } return this.get(`/api/v1/timelines/home?${params}`, extra); @@ -1007,10 +1095,18 @@ export class LysandClient extends BaseClient { const params = new URLSearchParams(); if (options) { - if (options.limit) params.set("limit", options.limit.toString()); - if (options.local) params.set("local", "true"); - if (options.offset) params.set("offset", options.offset.toString()); - if (options.order) params.set("order", options.order); + if (options.limit) { + params.set("limit", options.limit.toString()); + } + if (options.local) { + params.set("local", "true"); + } + if (options.offset) { + params.set("offset", options.offset.toString()); + } + if (options.order) { + params.set("order", options.order); + } } return this.get(`/api/v1/directory?${params}`, extra); @@ -1078,7 +1174,9 @@ export class LysandClient extends BaseClient { 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}`); @@ -1117,10 +1215,18 @@ export class LysandClient extends BaseClient { const params = new URLSearchParams(); if (options) { - if (options.max_id) params.set("max_id", options.max_id); - if (options.min_id) params.set("min_id", options.min_id); - if (options.since_id) params.set("since_id", options.since_id); - if (options.limit) params.set("limit", options.limit.toString()); + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } } return this.get( @@ -1162,11 +1268,21 @@ export class LysandClient extends BaseClient { const params = new URLSearchParams(); if (options) { - if (options.max_id) params.set("max_id", options.max_id); - if (options.min_id) params.set("min_id", options.min_id); - if (options.since_id) params.set("since_id", options.since_id); - if (options.limit) params.set("limit", options.limit.toString()); - if (options.only_media) params.set("only_media", "true"); + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + if (options.only_media) { + params.set("only_media", "true"); + } } return this.get(`/api/v1/timelines/public?${params}`, extra); @@ -1223,9 +1339,15 @@ export class LysandClient extends BaseClient { const params = new URLSearchParams(); if (options) { - if (options.max_id) params.set("max_id", options.max_id); - if (options.since_id) params.set("since_id", options.since_id); - if (options.limit) params.set("limit", options.limit.toString()); + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } } return this.get(`/api/v1/mutes?${params}`); @@ -1268,17 +1390,26 @@ export class LysandClient extends BaseClient { const params = new URLSearchParams(); if (options) { - if (options.max_id) params.set("max_id", options.max_id); - if (options.min_id) params.set("min_id", options.min_id); - if (options.since_id) params.set("since_id", options.since_id); - if (options.limit) params.set("limit", options.limit.toString()); + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } if (options.exclude_types) { for (const type of options.exclude_types) { params.append("exclude_types[]", type); } } - if (options.account_id) + if (options.account_id) { params.set("account_id", options.account_id); + } } return this.get(`/api/v1/notifications?${params}`); @@ -1326,11 +1457,21 @@ export class LysandClient extends BaseClient { const params = new URLSearchParams(); if (options) { - if (options.max_id) params.set("max_id", options.max_id); - if (options.min_id) params.set("min_id", options.min_id); - if (options.since_id) params.set("since_id", options.since_id); - if (options.limit) params.set("limit", options.limit.toString()); - if (options.only_media) params.set("only_media", "true"); + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + if (options.only_media) { + params.set("only_media", "true"); + } } return this.get(`/api/v1/timelines/public?${params}`, extra); @@ -1388,7 +1529,9 @@ export class LysandClient extends BaseClient { } if (options) { - if (options.with_suspended) params.set("with_suspended", "true"); + if (options.with_suspended) { + params.set("with_suspended", "true"); + } } return this.get( @@ -1457,10 +1600,18 @@ export class LysandClient extends BaseClient { const params = new URLSearchParams(); if (options) { - if (options.max_id) params.set("max_id", options.max_id); - if (options.min_id) params.set("min_id", options.min_id); - if (options.since_id) params.set("since_id", options.since_id); - if (options.limit) params.set("limit", options.limit.toString()); + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } } return this.get( @@ -1501,9 +1652,15 @@ export class LysandClient extends BaseClient { const params = new URLSearchParams(); if (options) { - if (options.limit) params.set("limit", options.limit.toString()); - if (options.max_id) params.set("max_id", options.max_id); - if (options.since_id) params.set("since_id", options.since_id); + if (options.limit) { + params.set("limit", options.limit.toString()); + } + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } } return this.get( @@ -1533,9 +1690,15 @@ export class LysandClient extends BaseClient { const params = new URLSearchParams(); if (options) { - if (options.max_id) params.set("max_id", options.max_id); - if (options.since_id) params.set("since_id", options.since_id); - if (options.limit) params.set("limit", options.limit.toString()); + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } } return this.get( @@ -1565,9 +1728,15 @@ export class LysandClient extends BaseClient { const params = new URLSearchParams(); if (options) { - if (options.max_id) params.set("max_id", options.max_id); - if (options.since_id) params.set("since_id", options.since_id); - if (options.limit) params.set("limit", options.limit.toString()); + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } } return this.get( @@ -1611,7 +1780,9 @@ export class LysandClient extends BaseClient { 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/suggestions?${params}`); @@ -1654,12 +1825,24 @@ export class LysandClient extends BaseClient { const params = new URLSearchParams(); if (options) { - if (options.max_id) params.set("max_id", options.max_id); - if (options.min_id) params.set("min_id", options.min_id); - if (options.since_id) params.set("since_id", options.since_id); - if (options.limit) params.set("limit", options.limit.toString()); - if (options.local) params.set("local", "true"); - if (options.only_media) params.set("only_media", "true"); + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + if (options.local) { + params.set("local", "true"); + } + if (options.only_media) { + params.set("only_media", "true"); + } } return this.get( @@ -1848,18 +2031,18 @@ export class LysandClient extends BaseClient { * @param token will be get #fetchAccessToken */ public refreshToken( - client_id: string, - client_secret: string, - refresh_token: string, + clientId: string, + clientSecret: string, + refreshToken: string, extra?: RequestInit, ): Promise> { return this.post( "/oauth/token", { - client_id, - client_secret, + clientId, + clientSecret, grant_type: "refresh_token", - refresh_token, + refreshToken, }, extra, ); @@ -1900,7 +2083,7 @@ export class LysandClient extends BaseClient { * @param options Form Data */ public registerApp( - client_name: string, + clientName: string, options: { redirect_uris: string; scopes?: string; @@ -1910,7 +2093,7 @@ export class LysandClient extends BaseClient { ): Promise> { return this.post( "/api/v1/apps", - { client_name, ...options }, + { clientName, ...options }, extra, ); } @@ -1976,7 +2159,7 @@ export class LysandClient extends BaseClient { * @return Report. */ public report( - account_id: string, + accountId: string, options: { status_ids?: string[]; rule_ids?: string[]; @@ -1988,7 +2171,7 @@ export class LysandClient extends BaseClient { ): Promise> { return this.post( "/api/v1/reports", - { account_id, ...options }, + { accountId, ...options }, extra, ); } @@ -2002,14 +2185,14 @@ export class LysandClient extends BaseClient { * @param token will be get #fetchAccessToken */ public revokeToken( - client_id: string, - client_secret: string, + clientId: string, + clientSecret: string, token: string, extra?: RequestInit, ): Promise> { return this.post( "/oauth/revoke", - { client_id, client_secret, token }, + { clientId, clientSecret, token }, extra, ); } @@ -2044,12 +2227,12 @@ export class LysandClient extends BaseClient { */ public scheduleStatus( id: string, - scheduled_at?: Date, + scheduledAt?: Date, extra?: RequestInit, ): Promise> { return this.put( `/api/v1/scheduled_statuses/${id}`, - { scheduled_at: scheduled_at?.toISOString() }, + { scheduled_at: scheduledAt?.toISOString() }, extra, ); } @@ -2088,17 +2271,33 @@ export class LysandClient extends BaseClient { params.set("q", q); if (options) { - if (options.account_id) + if (options.account_id) { params.set("account_id", options.account_id); - if (options.exclude_unreviewed) + } + if (options.exclude_unreviewed) { params.set("exclude_unreviewed", "true"); - if (options.following) params.set("following", "true"); - if (options.limit) params.set("limit", options.limit.toString()); - if (options.max_id) params.set("max_id", options.max_id); - if (options.min_id) params.set("min_id", options.min_id); - if (options.offset) params.set("offset", options.offset.toString()); - if (options.resolve) params.set("resolve", "true"); - if (options.type) params.set("type", options.type); + } + if (options.following) { + params.set("following", "true"); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.min_id) { + params.set("min_id", options.min_id); + } + if (options.offset) { + params.set("offset", options.offset.toString()); + } + if (options.resolve) { + params.set("resolve", "true"); + } + if (options.type) { + params.set("type", options.type); + } } return this.get(`/api/v2/search?${params}`, extra); @@ -2131,11 +2330,21 @@ export class LysandClient extends BaseClient { params.set("q", q); if (options) { - if (options.following) params.set("following", "true"); - if (options.limit) params.set("limit", options.limit.toString()); - if (options.max_id) params.set("max_id", options.max_id); - if (options.resolve) params.set("resolve", "true"); - if (options.since_id) params.set("since_id", options.since_id); + if (options.following) { + params.set("following", "true"); + } + if (options.limit) { + params.set("limit", options.limit.toString()); + } + if (options.max_id) { + params.set("max_id", options.max_id); + } + if (options.resolve) { + params.set("resolve", "true"); + } + if (options.since_id) { + params.set("since_id", options.since_id); + } } return this.get(`/api/v1/accounts/search?${params}`, extra); diff --git a/client/types/account.ts b/client/types/account.ts index 3e8366b..0a9f43f 100644 --- a/client/types/account.ts +++ b/client/types/account.ts @@ -25,9 +25,9 @@ export type Account = { avatar_static: string; header: string; header_static: string; - emojis: Array; + emojis: Emoji[]; moved: Account | null; - fields: Array; + fields: Field[]; bot: boolean | null; source?: Source; role?: Role; diff --git a/client/types/announcement.ts b/client/types/announcement.ts index 5a4642c..0a3cb10 100644 --- a/client/types/announcement.ts +++ b/client/types/announcement.ts @@ -11,11 +11,11 @@ export type Announcement = { published_at: string; updated_at: string | null; read: boolean | null; - mentions: Array; - statuses: Array; - tags: Array; - emojis: Array; - reactions: Array; + mentions: AnnouncementAccount[]; + statuses: AnnouncementStatus[]; + tags: StatusTag[]; + emojis: Emoji[]; + reactions: AnnouncementReaction[]; }; export type AnnouncementAccount = { diff --git a/client/types/context.ts b/client/types/context.ts index 23f9643..c053f51 100644 --- a/client/types/context.ts +++ b/client/types/context.ts @@ -1,6 +1,6 @@ import type { Status } from "./status"; export type Context = { - ancestors: Array; - descendants: Array; + ancestors: Status[]; + descendants: Status[]; }; diff --git a/client/types/conversation.ts b/client/types/conversation.ts index 4a44c9b..5edf609 100644 --- a/client/types/conversation.ts +++ b/client/types/conversation.ts @@ -3,7 +3,7 @@ import type { Status } from "./status"; export type Conversation = { id: string; - accounts: Array; + accounts: Account[]; last_status: Status | null; unread: boolean; }; diff --git a/client/types/filter.ts b/client/types/filter.ts index a39928c..da97b41 100644 --- a/client/types/filter.ts +++ b/client/types/filter.ts @@ -1,7 +1,7 @@ export type Filter = { id: string; phrase: string; - context: Array; + context: FilterContext[]; expires_at: string | null; irreversible: boolean; whole_word: boolean; diff --git a/client/types/follow_request.ts b/client/types/follow_request.ts index edcc447..676b583 100644 --- a/client/types/follow_request.ts +++ b/client/types/follow_request.ts @@ -20,6 +20,6 @@ export type FollowRequest = { followers_count: number; following_count: number; statuses_count: number; - emojis: Array; - fields: Array; + emojis: Emoji[]; + fields: Field[]; }; diff --git a/client/types/lysand.ts b/client/types/lysand.ts index 83f59f5..1a84629 100644 --- a/client/types/lysand.ts +++ b/client/types/lysand.ts @@ -10,47 +10,48 @@ export type LysandRole = { // Last updated: 2024-06-07 export enum LysandRolePermissions { - MANAGE_NOTES = "notes", - MANAGE_OWN_NOTES = "owner:note", - VIEW_NOTES = "read:note", - VIEW_NOTE_LIKES = "read:note_likes", - VIEW_NOTE_BOOSTS = "read:note_boosts", - MANAGE_ACCOUNTS = "accounts", - MANAGE_OWN_ACCOUNT = "owner:account", - VIEW_ACCOUNT_FOLLOWS = "read:account_follows", - MANAGE_LIKES = "likes", - MANAGE_OWN_LIKES = "owner:like", - MANAGE_BOOSTS = "boosts", - MANAGE_OWN_BOOSTS = "owner:boost", - VIEW_ACCOUNTS = "read:account", - MANAGE_EMOJIS = "emojis", - VIEW_EMOJIS = "read:emoji", - MANAGE_OWN_EMOJIS = "owner:emoji", - MANAGE_MEDIA = "media", - MANAGE_OWN_MEDIA = "owner:media", - MANAGE_BLOCKS = "blocks", - MANAGE_OWN_BLOCKS = "owner:block", - MANAGE_FILTERS = "filters", - MANAGE_OWN_FILTERS = "owner:filter", - MANAGE_MUTES = "mutes", - MANAGE_OWN_MUTES = "owner:mute", - MANAGE_REPORTS = "reports", - MANAGE_OWN_REPORTS = "owner:report", - MANAGE_SETTINGS = "settings", - MANAGE_OWN_SETTINGS = "owner:settings", - MANAGE_ROLES = "roles", - MANAGE_NOTIFICATIONS = "notifications", - MANAGE_OWN_NOTIFICATIONS = "owner:notification", - MANAGE_FOLLOWS = "follows", - MANAGE_OWN_FOLLOWS = "owner:follow", - MANAGE_OWN_APPS = "owner:app", - SEARCH = "search", - VIEW_PUBLIC_TIMELINES = "public_timelines", - VIEW_PRIVATE_TIMELINES = "private_timelines", - IGNORE_RATE_LIMITS = "ignore_rate_limits", - IMPERSONATE = "impersonate", - MANAGE_INSTANCE = "instance", - MANAGE_INSTANCE_FEDERATION = "instance:federation", - MANAGE_INSTANCE_SETTINGS = "instance:settings", - OAUTH = "oauth", + ManageNotes = "notes", + ManageOwnNotes = "owner:note", + ViewNotes = "read:note", + ViewNoteLikes = "read:note_likes", + ViewNoteBoosts = "read:note_boosts", + ManageAccounts = "accounts", + ManageOwnAccount = "owner:account", + ViewAccountFollows = "read:account_follows", + ManageLikes = "likes", + ManageOwnLikes = "owner:like", + ManageBoosts = "boosts", + ManageOwnBoosts = "owner:boost", + ViewAccounts = "read:account", + ManageEmojis = "emojis", + ViewEmojis = "read:emoji", + ManageOwnEmojis = "owner:emoji", + ManageMedia = "media", + ManageOwnMedia = "owner:media", + ManageBlocks = "blocks", + ManageOwnBlocks = "owner:block", + ManageFilters = "filters", + ManageOwnFilters = "owner:filter", + ManageMutes = "mutes", + ManageOwnMutes = "owner:mute", + ManageReports = "reports", + ManageOwnReports = "owner:report", + ManageSettings = "settings", + ManageOwnSettings = "owner:settings", + ManageRoles = "roles", + ManageNotifications = "notifications", + ManageOwnNotifications = "owner:notification", + ManageFollows = "follows", + ManageOwnFollows = "owner:follow", + ManageOwnApps = "owner:app", + Search = "search", + ViewPublicTimelines = "public_timelines", + ViewPrimateTimelines = "private_timelines", + IgnoreRateLimits = "ignore_rate_limits", + Impersonate = "impersonate", + ManageInstance = "instance", + ManageInstanceFederation = "instance:federation", + ManageInstanceSettings = "instance:settings", + /** Users who do not have this permission will not be able to login! */ + OAuth = "oauth", } diff --git a/client/types/poll.ts b/client/types/poll.ts index 48fed02..33fef2f 100644 --- a/client/types/poll.ts +++ b/client/types/poll.ts @@ -4,7 +4,7 @@ export type Poll = { expired: boolean; multiple: boolean; votes_count: number; - options: Array; + options: PollOption[]; voted: boolean; }; diff --git a/client/types/reaction.ts b/client/types/reaction.ts index e7fffdf..7483269 100644 --- a/client/types/reaction.ts +++ b/client/types/reaction.ts @@ -6,6 +6,6 @@ export type Reaction = { name: string; url?: string; static_url?: string; - accounts?: Array; - account_ids?: Array; + accounts?: Account[]; + account_ids?: string[]; }; diff --git a/client/types/report.ts b/client/types/report.ts index 385e5d8..ee98aa3 100644 --- a/client/types/report.ts +++ b/client/types/report.ts @@ -4,8 +4,8 @@ export type Report = { id: string; action_taken: boolean; action_taken_at: string | null; - status_ids: Array | null; - rule_ids: Array | null; + status_ids: string[] | null; + rule_ids: string[] | null; // These parameters don't exist in Pleroma category: Category | null; comment: string | null; diff --git a/client/types/results.ts b/client/types/results.ts index 68ffd53..229f98e 100644 --- a/client/types/results.ts +++ b/client/types/results.ts @@ -3,7 +3,7 @@ import type { Status } from "./status"; import type { Tag } from "./tag"; export type Results = { - accounts: Array; - statuses: Array; - hashtags: Array; + accounts: Account[]; + statuses: Status[]; + hashtags: Tag[]; }; diff --git a/client/types/scheduled_status.ts b/client/types/scheduled_status.ts index e8f8a2a..69389ef 100644 --- a/client/types/scheduled_status.ts +++ b/client/types/scheduled_status.ts @@ -5,5 +5,5 @@ export type ScheduledStatus = { id: string; scheduled_at: string; params: StatusParams; - media_attachments: Array | null; + media_attachments: Attachment[] | null; }; diff --git a/client/types/source.ts b/client/types/source.ts index 161be04..cab190e 100644 --- a/client/types/source.ts +++ b/client/types/source.ts @@ -5,5 +5,5 @@ export type Source = { sensitive: boolean | null; language: string | null; note: string; - fields: Array; + fields: Field[]; }; diff --git a/client/types/status.ts b/client/types/status.ts index 16e33fd..8e0b912 100644 --- a/client/types/status.ts +++ b/client/types/status.ts @@ -29,15 +29,15 @@ export type Status = { sensitive: boolean; spoiler_text: string; visibility: StatusVisibility; - media_attachments: Array; - mentions: Array; - tags: Array; + media_attachments: Attachment[]; + mentions: Mention[]; + tags: StatusTag[]; card: Card | null; poll: Poll | null; application: Application | null; language: string | null; pinned: boolean | null; - emoji_reactions: Array; + emoji_reactions: Reaction[]; quote: Status | null; bookmarked: boolean; }; diff --git a/client/types/status_params.ts b/client/types/status_params.ts index 89babfb..56c1874 100644 --- a/client/types/status_params.ts +++ b/client/types/status_params.ts @@ -3,7 +3,7 @@ import type { StatusVisibility } from "./status"; export type StatusParams = { text: string; in_reply_to_id: string | null; - media_ids: Array | null; + media_ids: string[] | null; sensitive: boolean | null; spoiler_text: string | null; visibility: StatusVisibility | null; diff --git a/client/types/tag.ts b/client/types/tag.ts index da41dd3..6923742 100644 --- a/client/types/tag.ts +++ b/client/types/tag.ts @@ -3,6 +3,6 @@ import type { History } from "./history"; export type Tag = { name: string; url: string; - history: Array; + history: History[]; following?: boolean; }; diff --git a/federation/cryptography/index.ts b/federation/cryptography/index.ts index b386f90..0c35c18 100644 --- a/federation/cryptography/index.ts +++ b/federation/cryptography/index.ts @@ -10,9 +10,9 @@ type HttpVerb = | "OPTIONS" | "HEAD"; -const checkEvironmentSupport = async () => { +const checkEvironmentSupport = () => { // Check if WebCrypto is supported - if (!globalThis.crypto || !globalThis.crypto.subtle) { + if (!globalThis.crypto?.subtle) { throw new Error("WebCrypto is not supported in this environment"); } @@ -27,9 +27,9 @@ const checkEvironmentSupport = async () => { export class SignatureValidator { /** * Creates a new instance of SignatureValidator. - * @param public_key The public key used for signature verification. + * @param publicKey The public key used for signature verification. */ - constructor(private public_key: CryptoKey) { + constructor(private publicKey: CryptoKey) { checkEvironmentSupport(); } @@ -114,9 +114,9 @@ export class SignatureValidator { ].filter(Boolean); // Check if all headers are present - if (!signature || !date || !method || !url || !body) { + if (!(signature && date && method && url && body)) { // Say which headers are missing - throw TypeError( + throw new TypeError( `Headers are missing in request: ${missingHeaders.join( ", ", )}`, @@ -124,7 +124,7 @@ export class SignatureValidator { } if (signature.split("signature=").length < 2) { - throw TypeError( + throw new TypeError( "Invalid Signature header (wrong format or missing signature)", ); } @@ -134,7 +134,7 @@ export class SignatureValidator { .replace(/"/g, ""); if (!extractedSignature) { - throw TypeError( + throw new TypeError( "Invalid Signature header (wrong format or missing signature)", ); } @@ -148,8 +148,8 @@ export class SignatureValidator { ); } - if (!date || !method || !url || !body) { - throw TypeError( + if (!(date && method && url && body)) { + throw new TypeError( "Missing or empty required parameters: date, method, url or body", ); } @@ -172,7 +172,7 @@ export class SignatureValidator { // Check if signed string is valid const isValid = await crypto.subtle.verify( "Ed25519", - this.public_key, + this.publicKey, Buffer.from(signature, "base64"), new TextEncoder().encode(expectedSignedString), ); @@ -302,8 +302,8 @@ export class SignatureConstructor { return { request, signedString }; } - if (!url || !body || !headers) { - throw TypeError( + if (!(url && body && headers)) { + throw new TypeError( "Missing or empty required parameters: url, body or headers", ); } diff --git a/federation/http/index.ts b/federation/http/index.ts index a905d0d..af4ac1f 100644 --- a/federation/http/index.ts +++ b/federation/http/index.ts @@ -69,27 +69,35 @@ export class RequestParserHandler { public async parseBody( callbacks: Partial>, ): Promise { - if (!this.body.type) throw new Error("Missing type field in body"); + if (!this.body.type) { + throw new Error("Missing type field in body"); + } switch (this.body.type) { case "Note": { const note = await this.validator.Note(this.body); - if (callbacks.note) return await callbacks.note(note); + if (callbacks.note) { + return await callbacks.note(note); + } break; } case "Patch": { const patch = await this.validator.Patch(this.body); - if (callbacks.patch) return await callbacks.patch(patch); + if (callbacks.patch) { + return await callbacks.patch(patch); + } break; } case "Follow": { const follow = await this.validator.Follow(this.body); - if (callbacks.follow) return await callbacks.follow(follow); + if (callbacks.follow) { + return await callbacks.follow(follow); + } break; } @@ -98,8 +106,9 @@ export class RequestParserHandler { this.body, ); - if (callbacks.followAccept) + if (callbacks.followAccept) { return await callbacks.followAccept(followAccept); + } break; } @@ -108,36 +117,45 @@ export class RequestParserHandler { this.body, ); - if (callbacks.followReject) + if (callbacks.followReject) { return await callbacks.followReject(followReject); + } break; } case "User": { const user = await this.validator.User(this.body); - if (callbacks.user) return await callbacks.user(user); + if (callbacks.user) { + return await callbacks.user(user); + } break; } case "Like": { const like = await this.validator.Like(this.body); - if (callbacks.like) return await callbacks.like(like); + if (callbacks.like) { + return await callbacks.like(like); + } break; } case "Dislike": { const dislike = await this.validator.Dislike(this.body); - if (callbacks.dislike) return await callbacks.dislike(dislike); + if (callbacks.dislike) { + return await callbacks.dislike(dislike); + } break; } case "Undo": { const undo = await this.validator.Undo(this.body); - if (callbacks.undo) return await callbacks.undo(undo); + if (callbacks.undo) { + return await callbacks.undo(undo); + } break; } @@ -146,16 +164,18 @@ export class RequestParserHandler { this.body, ); - if (callbacks.serverMetadata) + if (callbacks.serverMetadata) { return await callbacks.serverMetadata(serverMetadata); + } break; } case "Extension": { const extension = await this.validator.Extension(this.body); - if (callbacks.extension) + if (callbacks.extension) { return await callbacks.extension(extension); + } break; } diff --git a/federation/jsr.jsonc b/federation/jsr.jsonc index c45d3a8..a638048 100644 --- a/federation/jsr.jsonc +++ b/federation/jsr.jsonc @@ -3,6 +3,7 @@ "name": "@lysand-org/federation", "version": "0.0.0", "exports": { - ".": "./index.ts" + ".": "./index.ts", + "./types": "./schemas.ts" } } diff --git a/federation/package.json b/federation/package.json index 3275621..97074d3 100644 --- a/federation/package.json +++ b/federation/package.json @@ -41,6 +41,10 @@ ".": { "import": "./index.ts", "default": "./index.ts" + }, + "./types": { + "import": "./schemas.ts", + "default": "./schemas.ts" } }, "funding": { diff --git a/federation/schemas.ts b/federation/schemas.ts new file mode 100644 index 0000000..15d9122 --- /dev/null +++ b/federation/schemas.ts @@ -0,0 +1,51 @@ +import type { z } from "zod"; +import type { + ActionSchema, + ActorPublicKeyDataSchema, + ContentFormatSchema, + CustomEmojiExtensionSchema, + DislikeSchema, + EntitySchema, + ExtensionPropertySchema, + ExtensionSchema, + FollowAcceptSchema, + FollowRejectSchema, + FollowSchema, + LikeSchema, + NoteSchema, + PatchSchema, + PublicationSchema, + ReportSchema, + ServerMetadataSchema, + UndoSchema, + UserSchema, + VanityExtensionSchema, + VisibilitySchema, +} from "./schemas/base"; + +// biome-ignore lint/suspicious/noExplicitAny: Used only as a base type +type AnyZod = z.ZodType; + +type InferType = z.infer; + +export type Note = InferType; +export type Patch = InferType; +export type ActorPublicKeyData = InferType; +export type ExtensionProperty = InferType; +export type VanityExtension = InferType; +export type User = InferType; +export type Action = InferType; +export type Like = InferType; +export type Undo = InferType; +export type Dislike = InferType; +export type Follow = InferType; +export type FollowAccept = InferType; +export type FollowReject = InferType; +export type Extension = InferType; +export type Report = InferType; +export type ServerMetadata = InferType; +export type ContentFormat = InferType; +export type CustomEmojiExtension = InferType; +export type Visibility = InferType; +export type Publication = InferType; +export type Entity = InferType; diff --git a/federation/schemas/base.ts b/federation/schemas/base.ts index 05eb255..693928c 100644 --- a/federation/schemas/base.ts +++ b/federation/schemas/base.ts @@ -1,7 +1,7 @@ import { z } from "zod"; import { ContentFormatSchema } from "./content_format"; import { ExtensionPropertySchema } from "./extensions"; -import { CustomEmojiExtension } from "./extensions/custom_emojis"; +import { CustomEmojiExtensionSchema } from "./extensions/custom_emojis"; import { VanityExtensionSchema } from "./extensions/vanity"; import { extensionTypeRegex } from "./regex"; @@ -187,6 +187,6 @@ export { ReportSchema, ServerMetadataSchema, ContentFormatSchema, - CustomEmojiExtension, + CustomEmojiExtensionSchema, ExtensionPropertySchema, }; diff --git a/federation/schemas/extensions.ts b/federation/schemas/extensions.ts index da06308..2da03aa 100644 --- a/federation/schemas/extensions.ts +++ b/federation/schemas/extensions.ts @@ -1,6 +1,6 @@ import { z } from "zod"; -import { CustomEmojiExtension } from "./extensions/custom_emojis"; +import { CustomEmojiExtensionSchema } from "./extensions/custom_emojis"; export const ExtensionPropertySchema = z.object({ - "org.lysand:custom_emojis": CustomEmojiExtension.optional(), + "org.lysand:custom_emojis": CustomEmojiExtensionSchema.optional(), }); diff --git a/federation/schemas/extensions/custom_emojis.ts b/federation/schemas/extensions/custom_emojis.ts index cada5de..fb4ac0f 100644 --- a/federation/schemas/extensions/custom_emojis.ts +++ b/federation/schemas/extensions/custom_emojis.ts @@ -33,7 +33,7 @@ import { emojiRegex } from "../regex"; * // ... * } */ -export const CustomEmojiExtension = z.object({ +export const CustomEmojiExtensionSchema = z.object({ emojis: z.array( z.object({ name: z diff --git a/federation/tests/index.test.ts b/federation/tests/index.test.ts index 972510d..30a5021 100644 --- a/federation/tests/index.test.ts +++ b/federation/tests/index.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from "bun:test"; import { EntityValidator } from "../index"; describe("Package testing", () => { - it("should not validate a bad Note", async () => { + it("should not validate a bad Note", () => { const badObject = { IamBad: "Note", }; diff --git a/federation/validator/index.ts b/federation/validator/index.ts index 3a048a3..5c7c527 100644 --- a/federation/validator/index.ts +++ b/federation/validator/index.ts @@ -4,7 +4,7 @@ import { ActionSchema, ActorPublicKeyDataSchema, ContentFormatSchema, - CustomEmojiExtension, + CustomEmojiExtensionSchema, DislikeSchema, EntitySchema, ExtensionPropertySchema, @@ -54,10 +54,11 @@ type InferType = z.infer; * } * * // Types are also included for TypeScript users that don't use the extracted ones - * const noteObject: typeof EntityValidator.$Note = { - * type: "Note", - * // ... - * } + * import type { Note } from "@lysand-org/federation/types"; + * + * const note: Note = { + * ... + * }; */ export class EntityValidator { private async validate( @@ -71,34 +72,6 @@ export class EntityValidator { } } - declare static $Note: InferType; - declare static $Patch: InferType; - declare static $ActorPublicKeyData: InferType< - typeof ActorPublicKeyDataSchema - >; - declare static $ExtensionProperty: InferType< - typeof ExtensionPropertySchema - >; - declare static $VanityExtension: InferType; - declare static $User: InferType; - declare static $Action: InferType; - declare static $Like: InferType; - declare static $Undo: InferType; - declare static $Dislike: InferType; - declare static $Follow: InferType; - declare static $FollowAccept: InferType; - declare static $FollowReject: InferType; - declare static $Extension: InferType; - declare static $Report: InferType; - declare static $ServerMetadata: InferType; - declare static $ContentFormat: InferType; - declare static $CustomEmojiExtension: InferType< - typeof CustomEmojiExtension - >; - declare static $Visibility: InferType; - declare static $Publication: InferType; - declare static $Entity: InferType; - /** * Validates a Note entity. * @param data - The data to validate @@ -264,8 +237,8 @@ export class EntityValidator { */ public CustomEmojiExtension( data: unknown, - ): Promise> { - return this.validate(CustomEmojiExtension, data); + ): Promise> { + return this.validate(CustomEmojiExtensionSchema, data); } /**