diff --git a/.husky/pre-commit b/.husky/pre-commit index b3d4e050d..143e52c17 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1,5 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -cd packages/pl-fe && pnpm precommit && cd - \ No newline at end of file +cd packages/pl-api && pnpm precommit && cd - +cd packages/pl-fe && pnpm precommit && cd - diff --git a/package.json b/package.json index 1d0407bc4..538647f59 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,8 @@ "prepare": "husky" }, "devDependencies": { + "@oxfmt/binding-linux-x64-gnu": "^0.36.0", + "@oxlint/binding-linux-x64-gnu": "^1.51.0", "husky": "^9.0.0", "lint-staged": ">=10" }, diff --git a/packages/pl-api/.eslintignore b/packages/pl-api/.eslintignore deleted file mode 100644 index 256b5ff45..000000000 --- a/packages/pl-api/.eslintignore +++ /dev/null @@ -1,7 +0,0 @@ -/node_modules/** -/dist/** -/static/** -/public/** -/tmp/** -/coverage/** -/custom/** diff --git a/packages/pl-api/.eslintrc.json b/packages/pl-api/.eslintrc.json deleted file mode 100644 index 6bf945a6e..000000000 --- a/packages/pl-api/.eslintrc.json +++ /dev/null @@ -1,218 +0,0 @@ -{ - "root": true, - "extends": [ - "eslint:recommended", - "plugin:import/typescript", - "plugin:compat/recommended" - ], - "env": { - "browser": true, - "node": true, - "es6": true, - "jest": true - }, - "globals": { - "ATTACHMENT_HOST": false - }, - "plugins": [ - "@stylistic", - "import", - "promise", - "@typescript-eslint" - ], - "parserOptions": { - "sourceType": "module", - "ecmaFeatures": { - "experimentalObjectRestSpread": true - }, - "ecmaVersion": 2018 - }, - "settings": { - "import/extensions": [ - ".js", - ".cjs", - ".mjs", - ".ts" - ], - "import/ignore": [ - "node_modules", - "\\.(css|scss|json)$" - ], - "import/resolver": { - "typescript": true, - "node": true - }, - "polyfills": [ - "es:all", - "fetch", - "IntersectionObserver", - "Promise", - "ResizeObserver", - "URL", - "URLSearchParams" - ], - "tailwindcss": { - "config": "tailwind.config.ts" - } - }, - "rules": { - "brace-style": "error", - "comma-dangle": [ - "error", - "always-multiline" - ], - "comma-spacing": [ - "warn", - { - "before": false, - "after": true - } - ], - "comma-style": [ - "warn", - "last" - ], - "import/no-duplicates": "error", - "space-before-function-paren": [ - "error", - "never" - ], - "space-infix-ops": "error", - "space-in-parens": [ - "error", - "never" - ], - "keyword-spacing": "error", - "dot-notation": "error", - "eqeqeq": "error", - "indent": [ - "error", - 2, - { - "SwitchCase": 1, - "ignoredNodes": [ - "TemplateLiteral" - ] - } - ], - "key-spacing": [ - "error", - { - "mode": "minimum" - } - ], - "no-catch-shadow": "error", - "no-cond-assign": "error", - "no-console": [ - "warn", - { - "allow": [ - "error", - "warn" - ] - } - ], - "no-extra-semi": "error", - "no-const-assign": "error", - "no-fallthrough": "error", - "no-irregular-whitespace": "error", - "no-loop-func": "error", - "no-mixed-spaces-and-tabs": "error", - "no-nested-ternary": "warn", - "no-trailing-spaces": "error", - "no-undef": "error", - "no-unreachable": "error", - "no-unused-expressions": "error", - "no-unused-vars": "off", - "@typescript-eslint/no-unused-vars": [ - "error", - { - "vars": "all", - "args": "none", - "ignoreRestSiblings": true, - "caughtErrors": "none", - "argsIgnorePattern": "^_", - "varsIgnorePattern": "^_" - } - ], - "no-useless-escape": "warn", - "no-var": "error", - "object-curly-spacing": [ - "error", - "always" - ], - "padded-blocks": [ - "error", - { - "classes": "always" - } - ], - "prefer-const": "error", - "quotes": [ - "error", - "single" - ], - "semi": "error", - "space-unary-ops": [ - "error", - { - "words": true, - "nonwords": false - } - ], - "strict": "off", - "valid-typeof": "error", - "import/extensions": [ - "error", - "always", - { - "js": "never", - "mjs": "ignorePackages", - "ts": "never" - } - ], - "import/newline-after-import": "error", - "import/no-extraneous-dependencies": "error", - "import/no-unresolved": "error", - "import/no-webpack-loader-syntax": "error", - "import/order": [ - "error", - { - "groups": [ - "builtin", - "external", - "internal", - "parent", - "sibling", - "index", - "object", - "type" - ], - "newlines-between": "always", - "alphabetize": { - "order": "asc" - } - } - ], - "@stylistic/member-delimiter-style": "error", - "promise/catch-or-return": "error", - "sort-imports": [ - "error", - { - "ignoreCase": true, - "ignoreDeclarationSort": true - } - ], - "eol-last": "error" - }, - "overrides": [ - { - "files": ["**/*.ts"], - "rules": { - "no-undef": "off", - "space-before-function-paren": "off" - }, - "parser": "@typescript-eslint/parser" - } - ] -} diff --git a/packages/pl-api/.oxfmtrc.json b/packages/pl-api/.oxfmtrc.json new file mode 100644 index 000000000..f3af56790 --- /dev/null +++ b/packages/pl-api/.oxfmtrc.json @@ -0,0 +1,11 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "ignorePatterns": [], + "printWidth": null, + "singleQuote": true, + "arrowParens": null, + "experimentalSortImports": { + "groups": ["builtin", "external", "internal", "parent", "sibling", "index", "object", "type"] + }, + "tabWidth": 2 +} diff --git a/packages/pl-api/.oxlintrc.json b/packages/pl-api/.oxlintrc.json new file mode 100644 index 000000000..478574a87 --- /dev/null +++ b/packages/pl-api/.oxlintrc.json @@ -0,0 +1,61 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["typescript", "import", "promise"], + "categories": { + "correctness": "error", + "suspicious": "error", + "pedantic": "warn", + "perf": "warn" + }, + "rules": { + "max-dependencies": "off", + "max-lines": "off", + "max-lines-per-function": "off", + "no-inline-comments": "off", + "no-unused-vars": [ + "error", + { + "vars": "all", + "args": "none", + "ignoreRestSiblings": true, + "caughtErrors": "none", + "argsIgnorePattern": "^_", + "varsIgnorePattern": "^_" + } + ] + }, + "settings": { + "jsx-a11y": { + "polymorphicPropName": null, + "components": {}, + "attributes": {} + }, + "next": { + "rootDir": [] + }, + "react": { + "formComponents": [], + "linkComponents": [], + "version": null, + "componentWrapperFunctions": [] + }, + "jsdoc": { + "ignorePrivate": false, + "ignoreInternal": false, + "ignoreReplacesDocs": true, + "overrideReplacesDocs": true, + "augmentsExtendsReplacesDocs": false, + "implementsReplacesDocs": false, + "exemptDestructuredRootsFromChecks": false, + "tagNamePreference": {} + } + }, + "env": { + "builtin": true, + "browser": true, + "node": true, + "es6": true + }, + "globals": {}, + "ignorePatterns": [] +} diff --git a/packages/pl-api/README.md b/packages/pl-api/README.md index 0c5e3e4ab..db28bdfb9 100644 --- a/packages/pl-api/README.md +++ b/packages/pl-api/README.md @@ -9,6 +9,7 @@ A JavaScript library for interacting with Mastodon API-compatible servers, focus `pl-api` attempts to abstract out the implementation details when supporting different backends, implementing the same features in different ways. It uses [Valibot](https://valibot.dev/) to ensure type safety and normalize API responses. Example: + ```ts import { PlApiClient, type CreateApplicationParams } from 'pl-api'; @@ -37,8 +38,8 @@ For unsupported projects, it falls back to a basic feature set, though every met ## Projects using `pl-api` -* [Nicolium](https://codeberg.org/mkljczk/nicolium/src/branch/develop/packages/pl-fe) is a web client for Mastodon-compatible servers forked from Soapbox. It uses `pl-api` for API interactions. -* [`pl-hooks`](https://codeberg.org/mkljczk/nicolium/src/branch/develop/packages/pl-hooks) is a work-in-progress library utilizing `pl-api`. +- [Nicolium](https://codeberg.org/mkljczk/nicolium/src/branch/develop/packages/pl-fe) is a web client for Mastodon-compatible servers forked from Soapbox. It uses `pl-api` for API interactions. +- [`pl-hooks`](https://codeberg.org/mkljczk/nicolium/src/branch/develop/packages/pl-hooks) is a work-in-progress library utilizing `pl-api`. If you are using `pl-api` in your project, please open a pull request with a link to the project. diff --git a/packages/pl-api/lib/client.ts b/packages/pl-api/lib/client.ts index 5cbca163e..149f4b868 100644 --- a/packages/pl-api/lib/client.ts +++ b/packages/pl-api/lib/client.ts @@ -97,8 +97,23 @@ import { webPushSubscriptionSchema, } from './entities'; import { coerceObject, filteredArray } from './entities/utils'; -import { AKKOMA, type Features, getFeatures, GOTOSOCIAL, ICESHRIMP_NET, MITRA, PIXELFED, PLEROMA } from './features'; -import request, { getAsyncRefreshHeader, getNextLink, getPrevLink, type RequestBody, type RequestMeta } from './request'; +import { + AKKOMA, + type Features, + getFeatures, + GOTOSOCIAL, + ICESHRIMP_NET, + MITRA, + PIXELFED, + PLEROMA, +} from './features'; +import request, { + getAsyncRefreshHeader, + getNextLink, + getPrevLink, + type RequestBody, + type RequestMeta, +} from './request'; import { buildFullPath } from './utils/url'; import type { @@ -191,7 +206,10 @@ import type { MuteAccountParams, UpdateFilterParams, } from './params/filtering'; -import type { GetGroupedNotificationsParams, GetUnreadNotificationGroupCountParams } from './params/grouped-notifications'; +import type { + GetGroupedNotificationsParams, + GetUnreadNotificationGroupCountParams, +} from './params/grouped-notifications'; import type { CreateGroupParams, GetGroupBlocksParams, @@ -200,18 +218,9 @@ import type { UpdateGroupParams, } from './params/groups'; import type { ProfileDirectoryParams } from './params/instance'; -import type { - GetInteractionRequestsParams, -} from './params/interaction-requests'; -import type { - CreateListParams, - GetListAccountsParams, - UpdateListParams, -} from './params/lists'; -import type { - UpdateMediaParams, - UploadMediaParams, -} from './params/media'; +import type { GetInteractionRequestsParams } from './params/interaction-requests'; +import type { CreateListParams, GetListAccountsParams, UpdateListParams } from './params/lists'; +import type { UpdateMediaParams, UploadMediaParams } from './params/media'; import type { CreateBookmarkFolderParams, GetBookmarksParams, @@ -258,7 +267,12 @@ import type { GetStatusQuotesParams, GetStatusReferencesParams, } from './params/statuses'; -import type { CreateStoryParams, CreateStoryPollParams, CropStoryPhotoParams, StoryReportType } from './params/stories'; +import type { + CreateStoryParams, + CreateStoryPollParams, + CropStoryPhotoParams, + StoryReportType, +} from './params/stories'; import type { AntennaTimelineParams, BubbleTimelineParams, @@ -272,14 +286,19 @@ import type { SaveMarkersParams, WrenchedTimelineParams, } from './params/timelines'; -import type { - GetTrendingLinks, - GetTrendingStatuses, - GetTrendingTags, -} from './params/trends'; +import type { GetTrendingLinks, GetTrendingStatuses, GetTrendingTags } from './params/trends'; import type { PaginatedResponse } from './responses'; -const GROUPED_TYPES = ['favourite', 'reblog', 'emoji_reaction', 'event_reminder', 'participation_accepted', 'participation_request']; +const GROUPED_TYPES = [ + 'favourite', + 'reblog', + 'emoji_reaction', + 'event_reminder', + 'participation_accepted', + 'participation_request', +]; + +type EmptyObject = Record; interface PlApiClientConstructorOpts { /** Instance object to use by default, to be populated eg. from cache */ @@ -301,7 +320,6 @@ interface PlApiClientConstructorOpts { * @category Clients */ class PlApiClient { - baseURL: string; #accessToken?: string; #iceshrimpAccessToken?: string; @@ -326,14 +344,18 @@ class PlApiClient { * @param baseURL Mastodon API-compatible server URL * @param accessToken OAuth token for an authorized user */ - constructor(baseURL: string, accessToken?: string, { - instance, - fetchInstance, - fetchInstanceSignal, - onInstanceFetchSuccess, - onInstanceFetchError, - customAuthorizationToken, - }: PlApiClientConstructorOpts = {}) { + constructor( + baseURL: string, + accessToken?: string, + { + instance, + fetchInstance, + fetchInstanceSignal, + onInstanceFetchSuccess, + onInstanceFetchError, + customAuthorizationToken, + }: PlApiClientConstructorOpts = {}, + ) { this.baseURL = baseURL; this.#accessToken = accessToken; this.#customAuthorizationToken = customAuthorizationToken; @@ -342,27 +364,37 @@ class PlApiClient { this.#setInstance(instance); } if (fetchInstance) { - this.instance.getInstance().then((instance) => { - if (fetchInstanceSignal?.aborted) return; - onInstanceFetchSuccess?.(instance); - }).catch((error) => { - if (fetchInstanceSignal?.aborted) return; - onInstanceFetchError?.(error); - }); + this.instance + .getInstance() + .then((fetchedInstance) => { + if (fetchInstanceSignal?.aborted) return; + return onInstanceFetchSuccess?.(fetchedInstance); + }) + .catch((error) => { + if (fetchInstanceSignal?.aborted) return; + onInstanceFetchError?.(error); + }); } } - #paginatedGet = async (input: URL | RequestInfo, body: RequestBody, schema: v.BaseSchema>, isArray = true as IsArray): Promise> => { + #paginatedGet = async ( + input: URL | RequestInfo, + body: RequestBody, + schema: v.BaseSchema>, + isArray = true as IsArray, + ): Promise> => { const targetSchema = isArray ? filteredArray(schema) : schema; - const processResponse = (response: PlApiResponse) => ({ - previous: getMore(getPrevLink(response)), - next: getMore(getNextLink(response)), - items: v.parse(targetSchema, response.json), - partial: response.status === 206, - } as PaginatedResponse); + const processResponse = (response: PlApiResponse) => + ({ + previous: getMore(getPrevLink(response)), + next: getMore(getNextLink(response)), + items: v.parse(targetSchema, response.json), + partial: response.status === 206, + }) as PaginatedResponse; - const getMore = (input: string | null) => input ? () => this.request(input).then(processResponse) : null; + const getMore = (url: string | null) => + url ? () => this.request(url).then(processResponse) : null; const response = await this.request(input, body); @@ -387,10 +419,14 @@ class PlApiClient { // adminAccounts.forEach((adminAccount) => adminAccount.account = accounts.find(({ id }) => id === adminAccount.id) || null); return { - previous: !params.page ? null : () => this.#paginatedPleromaAccounts({ ...params, page: params.page! - 1 }), - next: response.json?.count > (params.page_size * ((params.page || 1) - 1) + response.json?.users?.length) - ? () => this.#paginatedPleromaAccounts({ ...params, page: (params.page || 0) + 1 }) + previous: params.page + ? () => this.#paginatedPleromaAccounts({ ...params, page: params.page! - 1 }) : null, + next: + response.json?.count > + params.page_size * ((params.page || 1) - 1) + response.json?.users?.length + ? () => this.#paginatedPleromaAccounts({ ...params, page: (params.page || 0) + 1 }) + : null, items: adminAccounts, partial: response.status === 206, total: response.json?.count, @@ -406,10 +442,14 @@ class PlApiClient { const response = await this.request('/api/v1/pleroma/admin/reports', { params }); return { - previous: !params.page ? null : () => this.#paginatedPleromaReports({ ...params, page: params.page! - 1 }), - next: response.json?.total > (params.page_size * ((params.page || 1) - 1) + response.json?.reports?.length) - ? () => this.#paginatedPleromaReports({ ...params, page: (params.page || 0) + 1 }) + previous: params.page + ? () => this.#paginatedPleromaReports({ ...params, page: params.page! - 1 }) : null, + next: + response.json?.total > + params.page_size * ((params.page || 1) - 1) + response.json?.reports?.length + ? () => this.#paginatedPleromaReports({ ...params, page: (params.page || 0) + 1 }) + : null, items: v.parse(filteredArray(adminReportSchema), response.json?.reports), partial: response.status === 206, total: response.json?.total, @@ -426,7 +466,9 @@ class PlApiClient { const response = await this.request('/api/v1/pleroma/admin/statuses', { params }); return { - previous: !params.page ? null : () => this.#paginatedPleromaStatuses({ ...params, page: params.page! - 1 }), + previous: params.page + ? () => this.#paginatedPleromaStatuses({ ...params, page: params.page! - 1 }) + : null, next: response.json?.length ? () => this.#paginatedPleromaStatuses({ ...params, page: (params.page || 0) + 1 }) : null, @@ -435,10 +477,13 @@ class PlApiClient { }; }; - #paginatedIceshrimpAccountsList = async (url: string, fn: (body: T) => Array): Promise> => { + #paginatedIceshrimpAccountsList = async ( + url: string, + fn: (body: T) => Array, + ): Promise> => { await this.#getIceshrimpAccessToken(); - const response = (await this.request(url)); + const response = await this.request(url); const ids = fn(response.json); const accounts = await this.accounts.getAccounts(ids); @@ -454,19 +499,24 @@ class PlApiClient { }; }; - #groupNotifications = ({ previous, next, items, ...response }: PaginatedResponse, params?: GetGroupedNotificationsParams): PaginatedResponse => { + #groupNotifications = ( + { previous, next, items, ...response }: PaginatedResponse, + params?: GetGroupedNotificationsParams, + ): PaginatedResponse => { const notificationGroups: Array = []; for (const notification of items) { let existingGroup: NotificationGroup | undefined; if ((params?.grouped_types || GROUPED_TYPES).includes(notification.type)) { - existingGroup = notificationGroups - .find(notificationGroup => - notificationGroup.type === notification.type - && ((notification.type === 'emoji_reaction' && notificationGroup.type === 'emoji_reaction') ? notification.emoji === notificationGroup.emoji : true) - // @ts-ignore - && notificationGroup.status_id === notification.status?.id, - ); + existingGroup = notificationGroups.find( + (notificationGroup) => + notificationGroup.type === notification.type && + (notification.type === 'emoji_reaction' && notificationGroup.type === 'emoji_reaction' + ? notification.emoji === notificationGroup.emoji + : true) && + // @ts-expect-error used optional chaining + notificationGroup.status_id === notification.status?.id, + ); } if (existingGroup) { @@ -475,7 +525,7 @@ class PlApiClient { existingGroup.sample_account_ids.push(notification.account.id); } else { notificationGroups.push({ - ...(omit(notification, ['account', 'status', 'target'])), + ...omit(notification, ['account', 'status', 'target']), group_key: notification.id, notifications_count: 1, most_recent_notification_id: notification.id, @@ -483,25 +533,30 @@ class PlApiClient { page_max_id: notification.id, latest_page_notification_at: notification.created_at, sample_account_ids: [notification.account.id], - // @ts-ignore + // @ts-expect-error used optional chaining status_id: notification.status?.id, - // @ts-ignore + // @ts-expect-error used optional chaining target_id: notification.target?.id, }); } } const groupedNotificationsResults: GroupedNotificationsResults = { - accounts: Object.values(items.reduce>((accounts, notification) => { - accounts[notification.account.id] = notification.account; - if ('target' in notification) accounts[notification.target.id] = notification.target; + accounts: Object.values( + items.reduce>((accounts, notification) => { + accounts[notification.account.id] = notification.account; + if ('target' in notification) accounts[notification.target.id] = notification.target; - return accounts; - }, {})), - statuses: Object.values(items.reduce>((statuses, notification) => { - if ('status' in notification && notification.status) statuses[notification.status.id] = notification.status; - return statuses; - }, {})), + return accounts; + }, {}), + ), + statuses: Object.values( + items.reduce>((statuses, notification) => { + if ('status' in notification && notification.status) + statuses[notification.status.id] = notification.status; + return statuses; + }, {}), + ), notification_groups: notificationGroups, }; @@ -557,35 +612,39 @@ class PlApiClient { */ getToken: async (params: GetTokenParams) => { if (this.features.version.software === ICESHRIMP_NET && params.grant_type === 'password') { - const loginResponse = (await this.request<{ - token: string; - }>('/api/iceshrimp/auth/login', { - method: 'POST', - body: { - username: params.username, - password: params.password, - }, - })).json; + const loginResponse = ( + await this.request<{ + token: string; + }>('/api/iceshrimp/auth/login', { + method: 'POST', + body: { + username: params.username, + password: params.password, + }, + }) + ).json; this.#iceshrimpAccessToken = loginResponse.token; - const mastodonTokenResponse = (await this.request<{ - id: string; - token: string; - created_at: string; - scopes: Array; - }>('/api/iceshrimp/sessions/mastodon', { - method: 'POST', - body: { - appName: params.client_id, - scopes: params.scope?.split(' '), - flags: { - supportsHtmlFormatting: true, - autoDetectQuotes: false, - isPleroma: true, - supportsInlineMedia: true, + const mastodonTokenResponse = ( + await this.request<{ + id: string; + token: string; + created_at: string; + scopes: Array; + }>('/api/iceshrimp/sessions/mastodon', { + method: 'POST', + body: { + appName: params.client_id, + scopes: params.scope?.split(' '), + flags: { + supportsHtmlFormatting: true, + autoDetectQuotes: false, + isPleroma: true, + supportsInlineMedia: true, + }, }, - }, - })).json; + }) + ).json; return v.parse(tokenSchema, { access_token: mastodonTokenResponse.token, @@ -594,11 +653,14 @@ class PlApiClient { created_at: new Date(mastodonTokenResponse.created_at).getTime(), id: mastodonTokenResponse.id, }); - } else { - const response = await this.request('/oauth/token', { method: 'POST', body: params, contentType: '' }); - - return v.parse(tokenSchema, { scope: params.scope || '', ...response.json }); } + const response = await this.request('/oauth/token', { + method: 'POST', + body: params, + contentType: '', + }); + + return v.parse(tokenSchema, { scope: params.scope || '', ...response.json }); }, /** @@ -607,11 +669,15 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/oauth/#revoke} */ revokeToken: async (params: RevokeTokenParams) => { - const response = await this.request('/oauth/revoke', { method: 'POST', body: params, contentType: '' }); + const response = await this.request('/oauth/revoke', { + method: 'POST', + body: params, + contentType: '', + }); this.#socket?.close(); - return response.json as {}; + return response.json; }, /** @@ -634,13 +700,19 @@ class PlApiClient { /** * Get a new captcha * @see {@link https://docs.pleroma.social/backend/development/API/pleroma_api/#apiv1pleromacaptcha} - */ + */ getCaptcha: async () => { const response = await this.request('/api/pleroma/captcha'); - return v.parse(v.intersect([v.object({ - type: v.string(), - }), v.record(v.string(), v.any())]), response.json); + return v.parse( + v.intersect([ + v.object({ + type: v.string(), + }), + v.record(v.string(), v.any()), + ]), + response.json, + ); }, mfaChallenge: async (params: MfaChallengeParams) => { @@ -652,9 +724,12 @@ class PlApiClient { public readonly emails = { resendConfirmationEmail: async (email: string) => { - const response = await this.request('/api/v1/emails/confirmations', { method: 'POST', body: { email } }); + const response = await this.request('/api/v1/emails/confirmations', { + method: 'POST', + body: { email }, + }); - return response.json as {}; + return response.json; }, }; @@ -688,7 +763,7 @@ class PlApiClient { * Statuses posted to the given account. * @see {@link https://docs.joinmastodon.org/methods/accounts/#statuses} */ - getAccountStatuses: async (accountId: string, params?: GetAccountStatusesParams) => + getAccountStatuses: (accountId: string, params?: GetAccountStatusesParams) => this.#paginatedGet(`/api/v1/accounts/${accountId}/statuses`, { params }, statusSchema), /** @@ -696,7 +771,7 @@ class PlApiClient { * Accounts which follow the given account, if network is not hidden by the account owner. * @see {@link https://docs.joinmastodon.org/methods/accounts/#followers} */ - getAccountFollowers: async (accountId: string, params?: GetAccountFollowersParams) => + getAccountFollowers: (accountId: string, params?: GetAccountFollowersParams) => this.#paginatedGet(`/api/v1/accounts/${accountId}/followers`, { params }, accountSchema), /** @@ -704,7 +779,7 @@ class PlApiClient { * Accounts which the given account is following, if network is not hidden by the account owner. * @see {@link https://docs.joinmastodon.org/methods/accounts/#following} */ - getAccountFollowing: async (accountId: string, params?: GetAccountFollowingParams) => + getAccountFollowing: (accountId: string, params?: GetAccountFollowingParams) => this.#paginatedGet(`/api/v1/accounts/${accountId}/following`, { params }, accountSchema), /** @@ -712,7 +787,7 @@ class PlApiClient { * * Requires features{@link Features.subscriptions}. */ - getAccountSubscribers: async (accountId: string, params?: GetAccountSubscribersParams) => + getAccountSubscribers: (accountId: string, params?: GetAccountSubscribersParams) => this.#paginatedGet(`/api/v1/accounts/${accountId}/subscribers`, { params }, accountSchema), /** @@ -777,7 +852,10 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/accounts/#follow} */ followAccount: async (accountId: string, params?: FollowAccountParams) => { - const response = await this.request(`/api/v1/accounts/${accountId}/follow`, { method: 'POST', body: params }); + const response = await this.request(`/api/v1/accounts/${accountId}/follow`, { + method: 'POST', + body: params, + }); return v.parse(relationshipSchema, response.json); }, @@ -788,7 +866,9 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/accounts/#unfollow} */ unfollowAccount: async (accountId: string) => { - const response = await this.request(`/api/v1/accounts/${accountId}/unfollow`, { method: 'POST' }); + const response = await this.request(`/api/v1/accounts/${accountId}/unfollow`, { + method: 'POST', + }); return v.parse(relationshipSchema, response.json); }, @@ -799,7 +879,9 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/accounts/#remove_from_followers} */ removeAccountFromFollowers: async (accountId: string) => { - const response = await this.request(`/api/v1/accounts/${accountId}/remove_from_followers`, { method: 'POST' }); + const response = await this.request(`/api/v1/accounts/${accountId}/remove_from_followers`, { + method: 'POST', + }); return v.parse(relationshipSchema, response.json); }, @@ -821,7 +903,9 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/accounts/#unpin} */ unpinAccount: async (accountId: string) => { - const response = await this.request(`/api/v1/accounts/${accountId}/unpin`, { method: 'POST' }); + const response = await this.request(`/api/v1/accounts/${accountId}/unpin`, { + method: 'POST', + }); return v.parse(relationshipSchema, response.json); }, @@ -832,7 +916,10 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/accounts/#note} */ updateAccountNote: async (accountId: string, comment: string) => { - const response = await this.request(`/api/v1/accounts/${accountId}/note`, { method: 'POST', body: { comment } }); + const response = await this.request(`/api/v1/accounts/${accountId}/note`, { + method: 'POST', + body: { comment }, + }); return v.parse(relationshipSchema, response.json); }, @@ -843,7 +930,9 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/accounts/#relationships} */ getRelationships: async (accountIds: string[], params?: GetRelationshipsParams) => { - const response = await this.request('/api/v1/accounts/relationships', { params: { ...params, id: accountIds } }); + const response = await this.request('/api/v1/accounts/relationships', { + params: { ...params, id: accountIds }, + }); return v.parse(filteredArray(relationshipSchema), response.json); }, @@ -859,16 +948,29 @@ class PlApiClient { let response: any; if (this.features.version.software === PIXELFED) { - response = []; - for (const accountId of accountIds) { - const accounts = (await this.request(`/api/v1.1/accounts/mutuals/${accountId}`)).json; - response.push({ - id: accountId, - accounts, - }); - } + const settledResponse = await Promise.allSettled( + accountIds.map(async (accountId) => { + const accounts = (await this.request(`/api/v1.1/accounts/mutuals/${accountId}`)).json; + + return { + id: accountId, + accounts, + }; + }), + ); + + response = settledResponse.map((result, index) => + result.status === 'fulfilled' + ? result.value + : { + id: accountIds[index], + accounts: [], + }, + ); } else { - response = (await this.request('/api/v1/accounts/familiar_followers', { params: { id: accountIds } })).json; + response = ( + await this.request('/api/v1/accounts/familiar_followers', { params: { id: accountIds } }) + ).json; } return v.parse(filteredArray(familiarFollowersSchema), response); @@ -880,7 +982,10 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/accounts/#search} */ searchAccounts: async (q: string, params?: SearchAccountParams, meta?: RequestMeta) => { - const response = await this.request('/api/v1/accounts/search', { ...meta, params: { ...params, q } }); + const response = await this.request('/api/v1/accounts/search', { + ...meta, + params: { ...params, q }, + }); return v.parse(filteredArray(accountSchema), response.json); }, @@ -919,7 +1024,7 @@ class PlApiClient { * @see {@link https://docs.pleroma.social/backend/development/API/pleroma_api/#apiv1pleromaaccountsidendorsements} * @see {@link https://docs.joinmastodon.org/methods/accounts/endorsements} */ - getAccountEndorsements: async (accountId: string, params?: GetAccountEndorsementsParams) => + getAccountEndorsements: (accountId: string, params?: GetAccountEndorsementsParams) => this.#paginatedGet( `/api/v1/${[PLEROMA].includes(this.features.version.software as string) ? 'pleroma/' : ''}accounts/${accountId}/endorsements`, { params }, @@ -944,8 +1049,12 @@ class PlApiClient { * Requires features{@link Features.publicFavourites}. * @see {@link https://docs.pleroma.social/backend/development/API/pleroma_api/#apiv1pleromaaccountsidfavourites} */ - getAccountFavourites: async (accountId: string, params?: GetAccountFavouritesParams) => - this.#paginatedGet(`/api/v1/pleroma/accounts/${accountId}/favourites`, { params }, statusSchema), + getAccountFavourites: (accountId: string, params?: GetAccountFavouritesParams) => + this.#paginatedGet( + `/api/v1/pleroma/accounts/${accountId}/favourites`, + { params }, + statusSchema, + ), /** * Interact with profile or status from remote account @@ -956,13 +1065,19 @@ class PlApiClient { * @see {@link https://docs.pleroma.social/backend/development/API/pleroma_api/#apiv1pleromaremote_interaction} */ remoteInteraction: async (ap_id: string, profile: string) => { - const response = await this.request('/api/v1/pleroma/remote_interaction', { method: 'POST', body: { ap_id, profile } }); + const response = await this.request('/api/v1/pleroma/remote_interaction', { + method: 'POST', + body: { ap_id, profile }, + }); if (response.json?.error) throw response.json.error; - return v.parse(v.object({ - url: v.string(), - }), response.json); + return v.parse( + v.object({ + url: v.string(), + }), + response.json, + ); }, /** @@ -975,14 +1090,20 @@ class PlApiClient { let response; switch (this.features.version.software) { case ICESHRIMP_NET: - response = await this.request('/api/v1/bite', { method: 'POST', body: accountId }); + response = await this.request('/api/v1/bite', { + method: 'POST', + body: accountId, + }); break; default: - response = await this.request('/api/v1/bite', { method: 'POST', params: { id: accountId } }); + response = await this.request('/api/v1/bite', { + method: 'POST', + params: { id: accountId }, + }); break; } - return response.json as {}; + return response.json; }, /** @@ -991,8 +1112,12 @@ class PlApiClient { * Requires features{@link Features.scrobbles} * @see {@link https://docs.pleroma.social/backend/development/API/pleroma_api/#get-apiv1pleromaaccountsidscrobbles} */ - getScrobbles: async (accountId: string, params?: GetScrobblesParams) => - this.#paginatedGet(`/api/v1/pleroma/accounts/${accountId}/scrobbles`, { params }, scrobbleSchema), + getScrobbles: (accountId: string, params?: GetScrobblesParams) => + this.#paginatedGet( + `/api/v1/pleroma/accounts/${accountId}/scrobbles`, + { params }, + scrobbleSchema, + ), /** * Creates a new Listen activity for an account @@ -1014,7 +1139,10 @@ class PlApiClient { * Requires features{@link Features.loadActivities} */ loadActivities: async (accountId: string) => { - const response = await this.request<{}>(`/api/v1/accounts/${accountId}/load_activities`, { method: 'POST' }); + const response = await this.request( + `/api/v1/accounts/${accountId}/load_activities`, + { method: 'POST' }, + ); return response.json; }, @@ -1026,7 +1154,7 @@ class PlApiClient { * Statuses the user has bookmarked. * @see {@link https://docs.joinmastodon.org/methods/bookmarks/#get} */ - getBookmarks: async (params?: GetBookmarksParams) => + getBookmarks: (params?: GetBookmarksParams) => this.#paginatedGet( this.features.bookmarkFoldersMultiple && params?.folder_id ? `/api/v1/bookmark_categories/${params.folder_id}/statuses` @@ -1040,14 +1168,14 @@ class PlApiClient { * Statuses the user has favourited. * @see {@link https://docs.joinmastodon.org/methods/favourites/#get} */ - getFavourites: async (params?: GetFavouritesParams) => + getFavourites: (params?: GetFavouritesParams) => this.#paginatedGet('/api/v1/favourites', { params }, statusSchema), /** * View pending follow requests * @see {@link https://docs.joinmastodon.org/methods/follow_requests/#get} */ - getFollowRequests: async (params?: GetFollowRequestsParams) => + getFollowRequests: (params?: GetFollowRequestsParams) => this.#paginatedGet('/api/v1/follow_requests', { params }, accountSchema), /** @@ -1055,11 +1183,11 @@ class PlApiClient { * * Requires features{@link Features.outgoingFollowRequests}. */ - getOutgoingFollowRequests: async (params?: GetFollowRequestsParams) => { + getOutgoingFollowRequests: (params?: GetFollowRequestsParams) => { if (this.features.version.software === ICESHRIMP_NET) { return this.#paginatedIceshrimpAccountsList( '/api/iceshrimp/follow_requests/outgoing', - (response: Array<{ user: {id: string } }>) => response.map(({ user }) => user.id), + (response: Array<{ user: { id: string } }>) => response.map(({ user }) => user.id), ); } @@ -1068,7 +1196,11 @@ class PlApiClient { return this.#paginatedGet('/api/v1/follow_requests/outgoing', { params }, accountSchema); default: - return this.#paginatedGet('/api/v1/pleroma/outgoing_follow_requests', { params }, accountSchema); + return this.#paginatedGet( + '/api/v1/pleroma/outgoing_follow_requests', + { params }, + accountSchema, + ); } }, @@ -1077,7 +1209,9 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/follow_requests/#accept} */ acceptFollowRequest: async (accountId: string) => { - const response = await this.request(`/api/v1/follow_requests/${accountId}/authorize`, { method: 'POST' }); + const response = await this.request(`/api/v1/follow_requests/${accountId}/authorize`, { + method: 'POST', + }); return v.parse(relationshipSchema, response.json); }, @@ -1087,7 +1221,9 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/follow_requests/#reject} */ rejectFollowRequest: async (accountId: string) => { - const response = await this.request(`/api/v1/follow_requests/${accountId}/reject`, { method: 'POST' }); + const response = await this.request(`/api/v1/follow_requests/${accountId}/reject`, { + method: 'POST', + }); return v.parse(relationshipSchema, response.json); }, @@ -1097,7 +1233,7 @@ class PlApiClient { * Accounts that the user is currently featuring on their profile. * @see {@link https://docs.joinmastodon.org/methods/endorsements/#get} */ - getEndorsements: async (params?: GetEndorsementsParams) => + getEndorsements: (params?: GetEndorsementsParams) => this.#paginatedGet('/api/v1/endorsements', { params }, accountSchema), /** @@ -1137,12 +1273,12 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/featured_tags/#unfeature} */ unfeatureTag: async (name: string) => { - const response = await this.request('/api/v1/featured_tags', { + const response = await this.request('/api/v1/featured_tags', { method: 'DELETE', body: { name }, }); - return response.json as {}; + return response.json; }, /** @@ -1165,7 +1301,7 @@ class PlApiClient { * Requires features{@link Features.followHashtags}. * @see {@link https://docs.joinmastodon.org/methods/followed_tags/#get} */ - getFollowedTags: async (params?: GetFollowedTagsParams) => + getFollowedTags: (params?: GetFollowedTagsParams) => this.#paginatedGet('/api/v1/followed_tags', { params }, tagSchema), /** @@ -1229,9 +1365,11 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/suggestions/#remove} */ dismissSuggestions: async (accountId: string) => { - const response = await this.request(`/api/v1/suggestions/${accountId}`, { method: 'DELETE' }); + const response = await this.request(`/api/v1/suggestions/${accountId}`, { + method: 'DELETE', + }); - return response.json as {}; + return response.json; }, /** @@ -1241,7 +1379,11 @@ class PlApiClient { * @see {@link https://docs.pleroma.social/backend/development/API/pleroma_api/#get-apiv1pleromabookmark_folders} */ getBookmarkFolders: async () => { - const response = await this.request(this.features.version.software === PLEROMA ? '/api/v1/pleroma/bookmark_folders' : '/api/v1/bookmark_categories'); + const response = await this.request( + this.features.version.software === PLEROMA + ? '/api/v1/pleroma/bookmark_folders' + : '/api/v1/bookmark_categories', + ); return v.parse(filteredArray(bookmarkFolderSchema), response.json); }, @@ -1255,7 +1397,9 @@ class PlApiClient { */ createBookmarkFolder: async (params: CreateBookmarkFolderParams) => { const response = await this.request( - this.features.version.software === PLEROMA ? '/api/v1/pleroma/bookmark_folders' : '/api/v1/bookmark_categories', + this.features.version.software === PLEROMA + ? '/api/v1/pleroma/bookmark_folders' + : '/api/v1/bookmark_categories', { method: 'POST', body: { title: params.name, ...params } }, ); @@ -1297,7 +1441,7 @@ class PlApiClient { * Requires features{@link Features.bookmarkFoldersMultiple}. */ addBookmarkToFolder: async (statusId: string, folderId: string) => { - const response = await this.request <{}>( + const response = await this.request( `/api/v1/bookmark_categories/${folderId}/statuses`, { method: 'POST', params: { status_ids: [statusId] } }, ); @@ -1309,7 +1453,7 @@ class PlApiClient { * Requires features{@link Features.bookmarkFoldersMultiple}. */ removeBookmarkFromFolder: async (statusId: string, folderId: string) => { - const response = await this.request<{}>( + const response = await this.request( `/api/v1/bookmark_categories/${folderId}/statuses`, { method: 'DELETE', params: { status_ids: [statusId] } }, ); @@ -1332,10 +1476,14 @@ class PlApiClient { body: { language: params.locale, birthday: params.date_of_birth, ...params }, }); - if ('identifier' in response.json) return v.parse(v.object({ - message: v.string(), - identifier: v.string(), - }), response.json); + if ('identifier' in response.json) + return v.parse( + v.object({ + message: v.string(), + identifier: v.string(), + }), + response.json, + ); return v.parse(tokenSchema, response.json); }, @@ -1376,7 +1524,13 @@ class PlApiClient { const response = await this.request('/api/v1/accounts/update_credentials', { method: 'PATCH', - contentType: (this.features.version.software === GOTOSOCIAL || this.features.version.software === ICESHRIMP_NET || params.avatar || params.header) ? '' : undefined, + contentType: + this.features.version.software === GOTOSOCIAL || + this.features.version.software === ICESHRIMP_NET || + params.avatar || + params.header + ? '' + : undefined, body: params, }); @@ -1458,7 +1612,10 @@ class PlApiClient { * @see {@link https://docs.pleroma.social/backend/development/API/pleroma_api/#add-alias-to-the-current-account} */ addAccountAlias: async (alias: string) => { - const response = await this.request('/api/pleroma/aliases', { method: 'PUT', body: { alias } }); + const response = await this.request('/api/pleroma/aliases', { + method: 'PUT', + body: { alias }, + }); return v.parse(v.object({ status: v.literal('success') }), response.json); }, @@ -1471,7 +1628,10 @@ class PlApiClient { * @see {@link https://docs.pleroma.social/backend/development/API/pleroma_api/#delete-alias-from-the-current-account} */ deleteAccountAlias: async (alias: string) => { - const response = await this.request('/api/pleroma/aliases', { method: 'DELETE', body: { alias } }); + const response = await this.request('/api/pleroma/aliases', { + method: 'DELETE', + body: { alias }, + }); return v.parse(v.object({ status: v.literal('success') }), response.json); }, @@ -1511,17 +1671,23 @@ class PlApiClient { switch (this.features.version.software) { case GOTOSOCIAL: - response = await this.request(`/api/v1/tokens/${oauthTokenId}/invalidate`, { method: 'POST' }); + response = await this.request(`/api/v1/tokens/${oauthTokenId}/invalidate`, { + method: 'POST', + }); break; case MITRA: - response = await this.request(`/api/v1/settings/sessions/${oauthTokenId}`, { method: 'DELETE' }); + response = await this.request(`/api/v1/settings/sessions/${oauthTokenId}`, { + method: 'DELETE', + }); break; default: - response = await this.request(`/api/oauth_tokens/${oauthTokenId}`, { method: 'DELETE' }); + response = await this.request(`/api/oauth_tokens/${oauthTokenId}`, { + method: 'DELETE', + }); break; } - return response.json as {}; + return response.json; }, /** @@ -1537,7 +1703,7 @@ class PlApiClient { switch (this.features.version.software) { case GOTOSOCIAL: - response = await this.request('/api/v1/user/password_change', { + response = await this.request('/api/v1/user/password_change', { method: 'POST', body: { old_password: current_password, @@ -1547,7 +1713,7 @@ class PlApiClient { break; case ICESHRIMP_NET: await this.#getIceshrimpAccessToken(); - response = await this.request('/api/iceshrimp/auth/change-password', { + response = await this.request('/api/iceshrimp/auth/change-password', { method: 'POST', body: { oldPassword: current_password, @@ -1556,13 +1722,13 @@ class PlApiClient { }); break; case MITRA: - response = await this.request('/api/v1/settings/change_password', { + response = await this.request('/api/v1/settings/change_password', { method: 'POST', body: { new_password }, }); break; case PIXELFED: - response = await this.request('/api/v1.1/accounts/change-password', { + response = await this.request('/api/v1.1/accounts/change-password', { method: 'POST', body: { current_password, @@ -1573,7 +1739,7 @@ class PlApiClient { if (response.redirected) throw response; break; default: - response = await this.request('/api/pleroma/change_password', { + response = await this.request('/api/pleroma/change_password', { method: 'POST', body: { password: current_password, @@ -1583,7 +1749,7 @@ class PlApiClient { }); } - return response.json as {}; + return response.json; }, /** @@ -1592,12 +1758,12 @@ class PlApiClient { * Requires features{@link Features.resetPassword}. */ resetPassword: async (email?: string, nickname?: string) => { - const response = await this.request('/auth/password', { + const response = await this.request('/auth/password', { method: 'POST', body: { email, nickname }, }); - return response.json as {}; + return response.json; }, /** @@ -1608,7 +1774,7 @@ class PlApiClient { switch (this.features.version.software) { case GOTOSOCIAL: - response = await this.request('/api/v1/user/email_change', { + response = await this.request('/api/v1/user/email_change', { method: 'POST', body: { new_email: email, @@ -1617,7 +1783,7 @@ class PlApiClient { }); break; default: - response = await this.request('/api/pleroma/change_email', { + response = await this.request('/api/pleroma/change_email', { method: 'POST', body: { email, @@ -1628,7 +1794,7 @@ class PlApiClient { if (response.json?.error) throw response.json.error; - return response.json as {}; + return response.json; }, /** @@ -1639,13 +1805,13 @@ class PlApiClient { switch (this.features.version.software) { case GOTOSOCIAL: - response = await this.request('/api/v1/accounts/delete', { + response = await this.request('/api/v1/accounts/delete', { method: 'POST', body: { password }, }); break; default: - response = await this.request('/api/pleroma/delete_account', { + response = await this.request('/api/pleroma/delete_account', { method: 'POST', body: { password }, }); @@ -1653,18 +1819,18 @@ class PlApiClient { if (response.json?.error) throw response.json.error; - return response.json as {}; + return response.json; }, /** * Requires features{@link Features.deleteAccountWithoutPassword}. */ deleteAccountWithoutPassword: async () => { - const response = await this.request('/api/v1/settings/delete_account', { + const response = await this.request('/api/v1/settings/delete_account', { method: 'POST', }); - return response.json as {}; + return response.json; }, /** @@ -1673,28 +1839,28 @@ class PlApiClient { * Requires features{@link Features.disableAccount}. */ disableAccount: async (password: string) => { - const response = await this.request('/api/pleroma/disable_account', { + const response = await this.request('/api/pleroma/disable_account', { method: 'POST', body: { password }, }); if (response.json?.error) throw response.json.error; - return response.json as {}; + return response.json; }, /** * Requires features{@link Features.accountMoving}. */ moveAccount: async (target_account: string, password: string) => { - const response = await this.request('/api/pleroma/move_account', { + const response = await this.request('/api/pleroma/move_account', { method: 'POST', body: { password, target_account }, }); if (response.json?.error) throw response.json.error; - return response.json as {}; + return response.json; }, mfa: { @@ -1717,12 +1883,15 @@ class PlApiClient { response = (await this.request('/api/pleroma/accounts/mfa')).json; } - return v.parse(v.object({ - settings: coerceObject({ - enabled: v.boolean(), - totp: v.boolean(), + return v.parse( + v.object({ + settings: coerceObject({ + enabled: v.boolean(), + totp: v.boolean(), + }), }), - }), response); + response, + ); }, /** @@ -1731,9 +1900,12 @@ class PlApiClient { getMfaBackupCodes: async () => { const response = await this.request('/api/pleroma/accounts/mfa/backup_codes'); - return v.parse(v.object({ - codes: v.array(v.string()), - }), response.json); + return v.parse( + v.object({ + codes: v.array(v.string()), + }), + response.json, + ); }, /** @@ -1753,10 +1925,13 @@ class PlApiClient { response = (await this.request(`/api/pleroma/accounts/mfa/setup/${method}`)).json; } - return v.parse(v.object({ - key: v.fallback(v.string(), ''), - provisioning_uri: v.string(), - }), response); + return v.parse( + v.object({ + key: v.fallback(v.string(), ''), + provisioning_uri: v.string(), + }), + response, + ); }, /** @@ -1767,18 +1942,23 @@ class PlApiClient { switch (this.features.version.software) { case GOTOSOCIAL: - response = await this.request('/api/v1/user/2fa/enable', { method: 'POST', body: { code } }); + response = await this.request('/api/v1/user/2fa/enable', { + method: 'POST', + body: { code }, + }); break; default: - response = (await this.request(`/api/pleroma/accounts/mfa/confirm/${method}`, { - method: 'POST', - body: { code, password }, - })).json; + response = ( + await this.request(`/api/pleroma/accounts/mfa/confirm/${method}`, { + method: 'POST', + body: { code, password }, + }) + ).json; } if (response?.error) throw response.error; - return response as {}; + return response as EmptyObject; }, /** @@ -1789,13 +1969,13 @@ class PlApiClient { switch (this.features.version.software) { case GOTOSOCIAL: - response = await this.request('/api/v1/user/2fa/disable', { + response = await this.request('/api/v1/user/2fa/disable', { method: 'POST', body: { password }, }); break; default: - response = await this.request(`/api/pleroma/accounts/mfa/${method}`, { + response = await this.request(`/api/pleroma/accounts/mfa/${method}`, { method: 'DELETE', body: { password }, }); @@ -1803,7 +1983,7 @@ class PlApiClient { if (response.json?.error) throw response.json.error; - return response.json as {}; + return response.json; }, }, @@ -1843,14 +2023,17 @@ class PlApiClient { }, /** - * Move followers from remote alias. (experimental?) - * - * Requires features{@link Features.importFollowers}. - */ + * Move followers from remote alias. (experimental?) + * + * Requires features{@link Features.importFollowers}. + */ importFollowers: async (list: File | string, actorId: string) => { const response = await this.request('/api/v1/settings/import_followers', { method: 'POST', - body: { from_actor_id: actorId, followers_csv: typeof list === 'string' ? list : await list.text() }, + body: { + from_actor_id: actorId, + followers_csv: typeof list === 'string' ? list : await list.text(), + }, }); return response.json; @@ -1961,10 +2144,10 @@ class PlApiClient { }, /** - * Export lists to CSV file - * - * Requires features{@link Features.exportLists}. - */ + * Export lists to CSV file + * + * Requires features{@link Features.exportLists}. + */ exportLists: async () => { const response = await this.request('/api/v1/exports/lists.csv', { method: 'GET', @@ -1974,10 +2157,10 @@ class PlApiClient { }, /** - * Export blocks to CSV file - * - * Requires features{@link Features.exportBlocks}. - */ + * Export blocks to CSV file + * + * Requires features{@link Features.exportBlocks}. + */ exportBlocks: async () => { const response = await this.request('/api/v1/exports/blocks.csv', { method: 'GET', @@ -1987,10 +2170,10 @@ class PlApiClient { }, /** - * Export mutes to CSV file - * - * Requires features{@link Features.exportMutes}. - */ + * Export mutes to CSV file + * + * Requires features{@link Features.exportMutes}. + */ exportMutes: async () => { const response = await this.request('/api/v1/exports/mutes.csv', { method: 'GET', @@ -2006,7 +2189,10 @@ class PlApiClient { * @see {@link https://docs.pleroma.social/backend/development/API/pleroma_api/#apipleromanotification_settings} */ updateNotificationSettings: async (params: UpdateNotificationSettingsParams) => { - const response = await this.request('/api/pleroma/notification_settings', { method: 'PUT', body: params }); + const response = await this.request('/api/pleroma/notification_settings', { + method: 'PUT', + body: params, + }); if (response.json?.error) throw response.json.error; @@ -2032,7 +2218,10 @@ class PlApiClient { * @see {@link https://docs.gotosocial.org/en/latest/api/swagger/} */ updateInteractionPolicies: async (params: UpdateInteractionPoliciesParams) => { - const response = await this.request('/api/v1/interaction_policies/defaults', { method: 'PATCH', body: params }); + const response = await this.request('/api/v1/interaction_policies/defaults', { + method: 'PATCH', + body: params, + }); return v.parse(interactionPoliciesSchema, response.json); }, @@ -2056,13 +2245,18 @@ class PlApiClient { * Requires features{@link Features.preferredFrontends}. */ setPreferredFrontend: async (frontendName: string) => { - const response = await this.request('/api/v1/akkoma/preferred_frontend', { method: 'PUT', body: { frontend_name: frontendName } }); + const response = await this.request('/api/v1/akkoma/preferred_frontend', { + method: 'PUT', + body: { frontend_name: frontendName }, + }); return v.parse(v.object({ frontend_name: v.string() }), response.json); }, authorizeIceshrimp: async () => { - const response = await this.request('/api/v1/accounts/authorize_iceshrimp', { method: 'POST' }); + const response = await this.request('/api/v1/accounts/authorize_iceshrimp', { + method: 'POST', + }); return response.json; }, @@ -2076,7 +2270,10 @@ class PlApiClient { * `duration` parameter requires features{@link Features.blocksDuration}. */ blockAccount: async (accountId: string, params?: BlockAccountParams) => { - const response = await this.request(`/api/v1/accounts/${accountId}/block`, { method: 'POST', body: params }); + const response = await this.request(`/api/v1/accounts/${accountId}/block`, { + method: 'POST', + body: params, + }); return v.parse(relationshipSchema, response.json); }, @@ -2087,7 +2284,9 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/accounts/#unblock} */ unblockAccount: async (accountId: string) => { - const response = await this.request(`/api/v1/accounts/${accountId}/unblock`, { method: 'POST' }); + const response = await this.request(`/api/v1/accounts/${accountId}/unblock`, { + method: 'POST', + }); return v.parse(relationshipSchema, response.json); }, @@ -2100,7 +2299,10 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/accounts/#mute} */ muteAccount: async (accountId: string, params?: MuteAccountParams) => { - const response = await this.request(`/api/v1/accounts/${accountId}/mute`, { method: 'POST', body: params }); + const response = await this.request(`/api/v1/accounts/${accountId}/mute`, { + method: 'POST', + body: params, + }); return v.parse(relationshipSchema, response.json); }, @@ -2113,7 +2315,9 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/accounts/#unmute} */ unmuteAccount: async (accountId: string) => { - const response = await this.request(`/api/v1/accounts/${accountId}/unmute`, { method: 'POST' }); + const response = await this.request(`/api/v1/accounts/${accountId}/unmute`, { + method: 'POST', + }); return v.parse(relationshipSchema, response.json); }, @@ -2125,14 +2329,14 @@ class PlApiClient { * Requires features{@link Features.mutes}. * @see {@link https://docs.joinmastodon.org/methods/mutes/#get} */ - getMutes: async (params?: GetMutesParams) => + getMutes: (params?: GetMutesParams) => this.#paginatedGet('/api/v1/mutes', { params }, mutedAccountSchema), /** * View blocked users * @see {@link https://docs.joinmastodon.org/methods/blocks/#get} */ - getBlocks: async (params?: GetBlocksParams) => + getBlocks: (params?: GetBlocksParams) => this.#paginatedGet('/api/v1/blocks', { params }, blockedAccountSchema), /** @@ -2140,7 +2344,7 @@ class PlApiClient { * View domains the user has blocked. * @see {@link https://docs.joinmastodon.org/methods/domain_blocks/#get} */ - getDomainBlocks: async (params?: GetDomainBlocksParams) => + getDomainBlocks: (params?: GetDomainBlocksParams) => this.#paginatedGet('/api/v1/domain_blocks', { params }, v.string()), /** @@ -2153,9 +2357,12 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/domain_blocks/#block} */ blockDomain: async (domain: string) => { - const response = await this.request('/api/v1/domain_blocks', { method: 'POST', body: { domain } }); + const response = await this.request('/api/v1/domain_blocks', { + method: 'POST', + body: { domain }, + }); - return response.json as {}; + return response.json; }, /** @@ -2164,12 +2371,12 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/domain_blocks/#unblock} */ unblockDomain: async (domain: string) => { - const response = await this.request('/api/v1/domain_blocks', { + const response = await this.request('/api/v1/domain_blocks', { method: 'DELETE', body: { domain }, }); - return response.json as {}; + return response.json; }, /** @@ -2180,7 +2387,9 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/filters/#get} */ getFilters: async () => { - const response = await this.request(this.features.filtersV2 ? '/api/v2/filters' : '/api/v1/filters'); + const response = await this.request( + this.features.filtersV2 ? '/api/v2/filters' : '/api/v1/filters', + ); return v.parse(filteredArray(filterSchema), response.json); }, @@ -2194,9 +2403,7 @@ class PlApiClient { */ getFilter: async (filterId: string) => { const response = await this.request( - this.features.filtersV2 - ? `/api/v2/filters/${filterId}` - : `/api/v1/filters/${filterId}`, + this.features.filtersV2 ? `/api/v2/filters/${filterId}` : `/api/v1/filters/${filterId}`, ); return v.parse(filterSchema, response.json); @@ -2211,19 +2418,18 @@ class PlApiClient { */ createFilter: async (params: CreateFilterParams) => { const { filtersV2 } = this.features; - const response = await this.request( - filtersV2 ? '/api/v2/filters' : '/api/v1/filters', - { - method: 'POST', - body: filtersV2 ? params : { - phrase: params.keywords_attributes[0]?.keyword, - context: params.context, - irreversible: params.filter_action === 'hide', - whole_word: params.keywords_attributes[0]?.whole_word, - expires_in: params.expires_in, - }, - }, - ); + const response = await this.request(filtersV2 ? '/api/v2/filters' : '/api/v1/filters', { + method: 'POST', + body: filtersV2 + ? params + : { + phrase: params.keywords_attributes[0]?.keyword, + context: params.context, + irreversible: params.filter_action === 'hide', + whole_word: params.keywords_attributes[0]?.whole_word, + expires_in: params.expires_in, + }, + }); return v.parse(filterSchema, response.json); }, @@ -2241,13 +2447,15 @@ class PlApiClient { filtersV2 ? `/api/v2/filters/${filterId}` : `/api/v1/filters/${filterId}`, { method: 'PUT', - body: filtersV2 ? params : { - phrase: params.keywords_attributes?.[0]?.keyword, - context: params.context, - irreversible: params.filter_action === 'hide', - whole_word: params.keywords_attributes?.[0]?.whole_word, - expires_in: params.expires_in, - }, + body: filtersV2 + ? params + : { + phrase: params.keywords_attributes?.[0]?.keyword, + context: params.context, + irreversible: params.filter_action === 'hide', + whole_word: params.keywords_attributes?.[0]?.whole_word, + expires_in: params.expires_in, + }, }, ); @@ -2262,14 +2470,12 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/filters/#delete} */ deleteFilter: async (filterId: string) => { - const response = await this.request( - this.features.filtersV2 - ? `/api/v2/filters/${filterId}` - : `/api/v1/filters/${filterId}`, + const response = await this.request( + this.features.filtersV2 ? `/api/v2/filters/${filterId}` : `/api/v1/filters/${filterId}`, { method: 'DELETE' }, ); - return response.json as {}; + return response.json; }, /** @@ -2338,9 +2544,11 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/filters/#keywords-delete} */ deleteFilterKeyword: async (filterId: string) => { - const response = await this.request(`/api/v2/filters/keywords/${filterId}`, { method: 'DELETE' }); + const response = await this.request(`/api/v2/filters/keywords/${filterId}`, { + method: 'DELETE', + }); - return response.json as {}; + return response.json; }, /** @@ -2393,11 +2601,12 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/filters/#statuses-remove} */ deleteFilterStatus: async (statusId: string) => { - const response = await this.request(`/api/v2/filters/statuses/${statusId}`, { method: 'DELETE' }); + const response = await this.request(`/api/v2/filters/statuses/${statusId}`, { + method: 'DELETE', + }); - return response.json as {}; + return response.json; }, - }; public readonly statuses = { @@ -2414,19 +2623,25 @@ class PlApiClient { const fixedParams: ExtendedCreateStatusParams = params; - if (params.content_type === 'text/markdown' && this.#instance.api_versions['kmyblue_markdown.fedibird.pl-api'] >= 1) { + if ( + params.content_type === 'text/markdown' && + this.#instance.api_versions['kmyblue_markdown.fedibird.pl-api'] >= 1 + ) { fixedParams.markdown = true; } if (params.visibility?.startsWith('api/v1/bookmark_categories')) { fixedParams.circle_id = params.visibility.slice(7); fixedParams.visibility = 'circle'; } - if (params.quote_id && this.#instance.api_versions.mastodon >= 7) params.quoted_status_id = params.quote_id; - else if (params.quoted_status_id && (this.#instance.api_versions.mastodon || 0) < 7) params.quote_id = params.quoted_status_id; + if (params.quote_id && this.#instance.api_versions.mastodon >= 7) + params.quoted_status_id = params.quote_id; + else if (params.quoted_status_id && (this.#instance.api_versions.mastodon || 0) < 7) + params.quote_id = params.quoted_status_id; - const input = params.preview && this.features.version.software === MITRA - ? '/api/v1/statuses/preview' - : '/api/v1/statuses'; + const input = + params.preview && this.features.version.software === MITRA + ? '/api/v1/statuses/preview' + : '/api/v1/statuses'; const response = await this.request(input, { method: 'POST', @@ -2441,9 +2656,10 @@ class PlApiClient { * Requires features{@link Features.createStatusPreview}. */ previewStatus: async (params: CreateStatusParams) => { - const input = this.features.version.software === PLEROMA || this.features.version.software === AKKOMA - ? '/api/v1/statuses' - : '/api/v1/statuses/preview'; + const input = + this.features.version.software === PLEROMA || this.features.version.software === AKKOMA + ? '/api/v1/statuses' + : '/api/v1/statuses/preview'; if (this.features.version.software === PLEROMA || this.features.version.software === AKKOMA) { params.preview = true; @@ -2474,9 +2690,11 @@ class PlApiClient { * * Requires features{@link Features.getStatuses}. * @see {@link https://docs.joinmastodon.org/methods/statuses/#index} - */ + */ getStatuses: async (statusIds: string[], params?: GetStatusesParams) => { - const response = await this.request('/api/v1/statuses', { params: { ...params, id: statusIds } }); + const response = await this.request('/api/v1/statuses', { + params: { ...params, id: statusIds }, + }); return v.parse(filteredArray(statusSchema), response.json); }, @@ -2489,7 +2707,10 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/statuses/#delete} */ deleteStatus: async (statusId: string, deleteMedia?: boolean) => { - const response = await this.request(`/api/v1/statuses/${statusId}`, { method: 'DELETE', params: { delete_media: deleteMedia } }); + const response = await this.request(`/api/v1/statuses/${statusId}`, { + method: 'DELETE', + params: { delete_media: deleteMedia }, + }); return v.parse(statusSourceSchema, response.json); }, @@ -2504,7 +2725,7 @@ class PlApiClient { const asyncRefreshHeader = getAsyncRefreshHeader(response); - return { asyncRefreshHeader, ...v.parse(contextSchema, response.json) } ; + return { asyncRefreshHeader, ...v.parse(contextSchema, response.json) }; }, /** @@ -2516,9 +2737,11 @@ class PlApiClient { let response; if (this.features.version.software === AKKOMA) { response = await this.request(`/api/v1/statuses/${statusId}/translations/${lang}`); - } else { - response = await this.request(`/api/v1/statuses/${statusId}/translate`, { method: 'POST', body: { lang } }); + response = await this.request(`/api/v1/statuses/${statusId}/translate`, { + method: 'POST', + body: { lang }, + }); } return v.parse(translationSchema, response.json); @@ -2530,7 +2753,10 @@ class PlApiClient { * Requires features{@link Features.lazyTranslations}. */ translateStatuses: async (statusIds: Array, lang: string) => { - const response = await this.request('/api/v1/pl/statuses/translate', { method: 'POST', body: { ids: statusIds, lang } }); + const response = await this.request('/api/v1/pl/statuses/translate', { + method: 'POST', + body: { ids: statusIds, lang }, + }); return v.parse(filteredArray(translationSchema), response.json); }, @@ -2540,7 +2766,7 @@ class PlApiClient { * View who boosted a given status. * @see {@link https://docs.joinmastodon.org/methods/statuses/#reblogged_by} */ - getRebloggedBy: async (statusId: string, params?: GetRebloggedByParams) => + getRebloggedBy: (statusId: string, params?: GetRebloggedByParams) => this.#paginatedGet(`/api/v1/statuses/${statusId}/reblogged_by`, { params }, accountSchema), /** @@ -2548,7 +2774,7 @@ class PlApiClient { * View who favourited a given status. * @see {@link https://docs.joinmastodon.org/methods/statuses/#favourited_by} */ - getFavouritedBy: async (statusId: string, params?: GetFavouritedByParams) => + getFavouritedBy: (statusId: string, params?: GetFavouritedByParams) => this.#paginatedGet(`/api/v1/statuses/${statusId}/favourited_by`, { params }, accountSchema), /** @@ -2557,7 +2783,9 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/statuses/#favourite} */ favouriteStatus: async (statusId: string) => { - const response = await this.request(`/api/v1/statuses/${statusId}/favourite`, { method: 'POST' }); + const response = await this.request(`/api/v1/statuses/${statusId}/favourite`, { + method: 'POST', + }); return v.parse(statusSchema, response.json); }, @@ -2568,7 +2796,9 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/statuses/#unfavourite} */ unfavouriteStatus: async (statusId: string) => { - const response = await this.request(`/api/v1/statuses/${statusId}/unfavourite`, { method: 'POST' }); + const response = await this.request(`/api/v1/statuses/${statusId}/unfavourite`, { + method: 'POST', + }); return v.parse(statusSchema, response.json); }, @@ -2581,7 +2811,10 @@ class PlApiClient { * Specifying reblog visibility requires features{@link Features.reblogVisibility}. */ reblogStatus: async (statusId: string, visibility?: string) => { - const response = await this.request(`/api/v1/statuses/${statusId}/reblog`, { method: 'POST', body: { visibility } }); + const response = await this.request(`/api/v1/statuses/${statusId}/reblog`, { + method: 'POST', + body: { visibility }, + }); return v.parse(statusSchema, response.json); }, @@ -2592,7 +2825,9 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/statuses/#unreblog} */ unreblogStatus: async (statusId: string) => { - const response = await this.request(`/api/v1/statuses/${statusId}/unreblog`, { method: 'POST' }); + const response = await this.request(`/api/v1/statuses/${statusId}/unreblog`, { + method: 'POST', + }); return v.parse(statusSchema, response.json); }, @@ -2603,10 +2838,16 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/statuses/#bookmark} */ bookmarkStatus: async (statusId: string, folderId?: string) => { - const response = await this.request(`/api/v1/statuses/${statusId}/bookmark`, { method: 'POST', body: { folder_id: folderId } }); + const response = await this.request(`/api/v1/statuses/${statusId}/bookmark`, { + method: 'POST', + body: { folder_id: folderId }, + }); if (folderId && this.features.bookmarkFoldersMultiple) { - await this.request(`/api/v1/bookmark_categories/${folderId}/statuses`, { method: 'POST', params: { status_ids: [statusId] } }); + await this.request(`/api/v1/bookmark_categories/${folderId}/statuses`, { + method: 'POST', + params: { status_ids: [statusId] }, + }); } return v.parse(statusSchema, response.json); @@ -2618,7 +2859,9 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/statuses/#unbookmark} */ unbookmarkStatus: async (statusId: string) => { - const response = await this.request(`/api/v1/statuses/${statusId}/unbookmark`, { method: 'POST' }); + const response = await this.request(`/api/v1/statuses/${statusId}/unbookmark`, { + method: 'POST', + }); return v.parse(statusSchema, response.json); }, @@ -2629,7 +2872,10 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/statuses/#revoke_quote} */ revokeQuote: async (statusId: string, quotingStatusId: string) => { - const response = await this.request(`/api/v1/statuses/${statusId}/quotes/${quotingStatusId}/revoke`, { method: 'POST' }); + const response = await this.request( + `/api/v1/statuses/${statusId}/quotes/${quotingStatusId}/revoke`, + { method: 'POST' }, + ); return v.parse(statusSchema, response.json); }, @@ -2651,7 +2897,9 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/statuses/#unmute} */ unmuteStatus: async (statusId: string) => { - const response = await this.request(`/api/v1/statuses/${statusId}/unmute`, { method: 'POST' }); + const response = await this.request(`/api/v1/statuses/${statusId}/unmute`, { + method: 'POST', + }); return v.parse(statusSchema, response.json); }, @@ -2690,11 +2938,17 @@ class PlApiClient { const fixedParams: ExtendedEditStatusParams = params; - if (params.content_type === 'text/markdown' && this.#instance.api_versions['kmyblue_markdown.fedibird.pl-api'] >= 1) { + if ( + params.content_type === 'text/markdown' && + this.#instance.api_versions['kmyblue_markdown.fedibird.pl-api'] >= 1 + ) { fixedParams.markdown = true; } - const response = await this.request(`/api/v1/statuses/${statusId}`, { method: 'PUT', body: params }); + const response = await this.request(`/api/v1/statuses/${statusId}`, { + method: 'PUT', + body: params, + }); return v.parse(statusSchema, response.json); }, @@ -2705,7 +2959,10 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/statuses/#edit_interaction_policy} */ editInteractionPolicy: async (statusId: string, params: EditInteractionPolicyParams) => { - const response = await this.request(`/api/v1/statuses/${statusId}`, { method: 'PUT', body: params }); + const response = await this.request(`/api/v1/statuses/${statusId}`, { + method: 'PUT', + body: params, + }); return v.parse(statusSchema, response.json); }, @@ -2743,24 +3000,32 @@ class PlApiClient { const apiVersions = this.#instance.api_versions; let response; - if (apiVersions['emoji_reactions.pleroma.pl-api'] >= 1 || this.features.version.software === ICESHRIMP_NET) { - response = await this.request(`/api/v1/pleroma/statuses/${statusId}/reactions${emoji ? `/${emoji}` : ''}`); + if ( + apiVersions['emoji_reactions.pleroma.pl-api'] >= 1 || + this.features.version.software === ICESHRIMP_NET + ) { + response = await this.request( + `/api/v1/pleroma/statuses/${statusId}/reactions${emoji ? `/${emoji}` : ''}`, + ); } else { if (apiVersions['emoji_reaction.fedibird.pl-api'] >= 1) { response = await this.request(`/api/v1/statuses/${statusId}/emoji_reactioned_by`); } else { - response = await this.request(`/api/v1/statuses/${statusId}/reactions`, { params: { emoji } }); + response = await this.request(`/api/v1/statuses/${statusId}/reactions`, { + params: { emoji }, + }); } response.json = response.json?.reduce((acc: Array, cur: any) => { if (emoji && cur.name !== emoji) return acc; - const existing = acc.find(reaction => reaction.name === cur.name); + const existing = acc.find((reaction) => reaction.name === cur.name); if (existing) { existing.accounts.push(cur.account); existing.account_ids.push(cur.account.id); existing.count += 1; - } else acc.push({ count: 1, accounts: [cur.account], account_ids: [cur.account.id], ...cur }); + } else + acc.push({ count: 1, accounts: [cur.account], account_ids: [cur.account.id], ...cur }); return acc; }, []); @@ -2780,10 +3045,19 @@ class PlApiClient { const apiVersions = this.#instance.api_versions; let response; - if (apiVersions['emoji_reactions.pleroma.pl-api'] >= 1 || this.features.version.software === MITRA) { - response = await this.request(`/api/v1/pleroma/statuses/${statusId}/reactions/${encodeURIComponent(emoji)}`, { method: 'PUT' }); + if ( + apiVersions['emoji_reactions.pleroma.pl-api'] >= 1 || + this.features.version.software === MITRA + ) { + response = await this.request( + `/api/v1/pleroma/statuses/${statusId}/reactions/${encodeURIComponent(emoji)}`, + { method: 'PUT' }, + ); } else { - response = await this.request(`/api/v1/statuses/${statusId}/react/${encodeURIComponent(emoji)}`, { method: 'POST' }); + response = await this.request( + `/api/v1/statuses/${statusId}/react/${encodeURIComponent(emoji)}`, + { method: 'POST' }, + ); } return v.parse(statusSchema, response.json); @@ -2799,10 +3073,18 @@ class PlApiClient { const apiVersions = this.#instance.api_versions; let response; - if (apiVersions['emoji_reactions.pleroma.pl-api'] >= 1 || this.features.version.software === MITRA) { - response = await this.request(`/api/v1/pleroma/statuses/${statusId}/reactions/${emoji}`, { method: 'DELETE' }); + if ( + apiVersions['emoji_reactions.pleroma.pl-api'] >= 1 || + this.features.version.software === MITRA + ) { + response = await this.request(`/api/v1/pleroma/statuses/${statusId}/reactions/${emoji}`, { + method: 'DELETE', + }); } else { - response = await this.request(`/api/v1/statuses/${statusId}/unreact/${encodeURIComponent(emoji)}`, { method: 'POST' }); + response = await this.request( + `/api/v1/statuses/${statusId}/unreact/${encodeURIComponent(emoji)}`, + { method: 'POST' }, + ); } return v.parse(statusSchema, response.json); @@ -2814,9 +3096,11 @@ class PlApiClient { * Requires features{@link Features.quotePosts}. * @see {@link https://docs.joinmastodon.org/methods/statuses/#quotes} */ - getStatusQuotes: async (statusId: string, params?: GetStatusQuotesParams) => + getStatusQuotes: (statusId: string, params?: GetStatusQuotesParams) => this.#paginatedGet( - this.#instance.api_versions.mastodon >= 7 ? `/api/v1/statuses/${statusId}/quotes` : `/api/v1/pleroma/statuses/${statusId}/quotes`, + this.#instance.api_versions.mastodon >= 7 + ? `/api/v1/statuses/${statusId}/quotes` + : `/api/v1/pleroma/statuses/${statusId}/quotes`, { params }, statusSchema, ), @@ -2827,7 +3111,7 @@ class PlApiClient { * Requires features{@link Features.statusDislikes}. * @see {@link https://github.com/friendica/friendica/blob/2024.06-rc/doc/API-Friendica.md#get-apifriendicastatusesiddisliked_by} */ - getDislikedBy: async (statusId: string) => + getDislikedBy: (statusId: string) => this.#paginatedGet(`/api/v1/statuses/${statusId}/disliked_by`, {}, accountSchema), /** @@ -2835,7 +3119,9 @@ class PlApiClient { * @see {@link https://github.com/friendica/friendica/blob/2024.06-rc/doc/API-Friendica.md#post-apifriendicastatusesiddislike} */ dislikeStatus: async (statusId: string) => { - const response = await this.request(`/api/friendica/statuses/${statusId}/dislike`, { method: 'POST' }); + const response = await this.request(`/api/friendica/statuses/${statusId}/dislike`, { + method: 'POST', + }); return v.parse(statusSchema, response.json); }, @@ -2845,15 +3131,17 @@ class PlApiClient { * @see {@link https://github.com/friendica/friendica/blob/2024.06-rc/doc/API-Friendica.md#post-apifriendicastatusesidundislike} */ undislikeStatus: async (statusId: string) => { - const response = await this.request(`/api/friendica/statuses/${statusId}/undislike`, { method: 'POST' }); + const response = await this.request(`/api/friendica/statuses/${statusId}/undislike`, { + method: 'POST', + }); return v.parse(statusSchema, response.json); }, - getStatusReferences: async (statusId: string, params?: GetStatusReferencesParams) => + getStatusReferences: (statusId: string, params?: GetStatusReferencesParams) => this.#paginatedGet(`/api/v1/statuses/${statusId}/referred_by`, { params }, statusSchema), - getStatusMentionedUsers: async (statusId: string, params?: GetStatusMentionedUsersParams) => + getStatusMentionedUsers: (statusId: string, params?: GetStatusMentionedUsersParams) => this.#paginatedGet(`/api/v1/statuses/${statusId}/mentioned_by`, { params }, accountSchema), /** @@ -2862,7 +3150,10 @@ class PlApiClient { * Requires features{@link Features.loadConversation}. */ loadConversation: async (statusId: string) => { - const response = await this.request <{}>(`/api/v1/statuses/${statusId}/load_conversation`, { method: 'POST' }); + const response = await this.request( + `/api/v1/statuses/${statusId}/load_conversation`, + { method: 'POST' }, + ); return response.json; }, @@ -2871,7 +3162,9 @@ class PlApiClient { * Requires features{@link Features.bookmarkFoldersMultiple}. */ getStatusBookmarkFolders: async (statusId: string) => { - const response = await this.request(`/api/v1/statuses/${statusId}/bookmark_categories`, { method: 'GET' }); + const response = await this.request(`/api/v1/statuses/${statusId}/bookmark_categories`, { + method: 'GET', + }); return v.parse(filteredArray(bookmarkFolderSchema), response.json); }, @@ -2911,7 +3204,8 @@ class PlApiClient { updateMedia: async (attachmentId: string, params: UpdateMediaParams) => { const response = await this.request(`/api/v1/media/${attachmentId}`, { method: 'PUT', - body: params, contentType: params.thumbnail ? '' : undefined, + body: params, + contentType: params.thumbnail ? '' : undefined, }); return v.parse(mediaAttachmentSchema, response.json); @@ -2925,7 +3219,7 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/media/delete} */ deleteMedia: async (attachmentId: string) => { - const response = await this.request<{}>(`/api/v1/media/${attachmentId}`, { + const response = await this.request(`/api/v1/media/${attachmentId}`, { method: 'DELETE', }); @@ -2951,7 +3245,10 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/polls/#vote} */ vote: async (pollId: string, choices: number[]) => { - const response = await this.request(`/api/v1/polls/${pollId}/votes`, { method: 'POST', body: { choices } }); + const response = await this.request(`/api/v1/polls/${pollId}/votes`, { + method: 'POST', + body: { choices }, + }); return v.parse(pollSchema, response.json); }, @@ -2962,7 +3259,7 @@ class PlApiClient { * View scheduled statuses * @see {@link https://docs.joinmastodon.org/methods/scheduled_statuses/#get} */ - getScheduledStatuses: async (params?: GetScheduledStatusesParams) => + getScheduledStatuses: (params?: GetScheduledStatusesParams) => this.#paginatedGet('/api/v1/scheduled_statuses', { params }, scheduledStatusSchema), /** @@ -2993,9 +3290,14 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/scheduled_statuses/#cancel} */ cancelScheduledStatus: async (scheduledStatusId: string) => { - const response = await this.request(`/api/v1/scheduled_statuses/${scheduledStatusId}`, { method: 'DELETE' }); + const response = await this.request( + `/api/v1/scheduled_statuses/${scheduledStatusId}`, + { + method: 'DELETE', + }, + ); - return response.json as {}; + return response.json; }, }; @@ -3053,9 +3355,11 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/conversations/#delete} */ deleteConversation: async (conversationId: string) => { - const response = await this.request(`/api/v1/conversations/${conversationId}`, { method: 'DELETE' }); + const response = await this.request(`/api/v1/conversations/${conversationId}`, { + method: 'DELETE', + }); - return response.json as {}; + return response.json; }, /** @@ -3063,7 +3367,9 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/conversations/#read} */ markConversationRead: async (conversationId: string) => { - const response = await this.request(`/api/v1/conversations/${conversationId}/read`, { method: 'POST' }); + const response = await this.request(`/api/v1/conversations/${conversationId}/read`, { + method: 'POST', + }); return v.parse(conversationSchema, response.json); }, @@ -3093,9 +3399,11 @@ class PlApiClient { /** * Requires features{@link Features.groups}. */ - groupTimeline: async (groupId: string, params?: GroupTimelineParams) => + groupTimeline: (groupId: string, params?: GroupTimelineParams) => this.#paginatedGet( - this.features.version.software === PIXELFED ? `/api/v0/groups/${groupId}/feed` : `/api/v1/timelines/group/${groupId}`, + this.features.version.software === PIXELFED + ? `/api/v0/groups/${groupId}/feed` + : `/api/v1/timelines/group/${groupId}`, { params }, statusSchema, ), @@ -3103,7 +3411,7 @@ class PlApiClient { /** * Requires features{@link Features.bubbleTimeline}. */ - bubbleTimeline: async (params?: BubbleTimelineParams) => + bubbleTimeline: (params?: BubbleTimelineParams) => this.#paginatedGet('/api/v1/timelines/bubble', { params }, statusSchema), /** @@ -3116,7 +3424,7 @@ class PlApiClient { /** * Requires features{@link Features.wrenchedTimeline}. */ - wrenchedTimeline: async (params?: WrenchedTimelineParams) => + wrenchedTimeline: (params?: WrenchedTimelineParams) => this.#paginatedGet('/api/v1/pleroma/timelines/wrenched', { params }, statusSchema), }; @@ -3160,7 +3468,10 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/lists/#update} */ updateList: async (listId: string, params: UpdateListParams) => { - const response = await this.request(`/api/v1/lists/${listId}`, { method: 'PUT', body: params }); + const response = await this.request(`/api/v1/lists/${listId}`, { + method: 'PUT', + body: params, + }); return v.parse(listSchema, response.json); }, @@ -3170,16 +3481,18 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/lists/#delete} */ deleteList: async (listId: string) => { - const response = await this.request(`/api/v1/lists/${listId}`, { method: 'DELETE' }); + const response = await this.request(`/api/v1/lists/${listId}`, { + method: 'DELETE', + }); - return response.json as {}; + return response.json; }, /** * View accounts in a list * @see {@link https://docs.joinmastodon.org/methods/lists/#accounts} */ - getListAccounts: async (listId: string, params?: GetListAccountsParams) => + getListAccounts: (listId: string, params?: GetListAccountsParams) => this.#paginatedGet(`/api/v1/lists/${listId}/accounts`, { params }, accountSchema), /** @@ -3188,11 +3501,12 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/lists/#accounts-add} */ addListAccounts: async (listId: string, accountIds: string[]) => { - const response = await this.request(`/api/v1/lists/${listId}/accounts`, { - method: 'POST', body: { account_ids: accountIds }, + const response = await this.request(`/api/v1/lists/${listId}/accounts`, { + method: 'POST', + body: { account_ids: accountIds }, }); - return response.json as {}; + return response.json; }, /** @@ -3201,11 +3515,12 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/lists/#accounts-remove} */ deleteListAccounts: async (listId: string, accountIds: string[]) => { - const response = await this.request(`/api/v1/lists/${listId}/accounts`, { - method: 'DELETE', body: { account_ids: accountIds }, + const response = await this.request(`/api/v1/lists/${listId}/accounts`, { + method: 'DELETE', + body: { account_ids: accountIds }, }); - return response.json as {}; + return response.json; }, /** @@ -3225,7 +3540,9 @@ class PlApiClient { * Requires features{@link Features.listsFavourite}. */ unfavouriteList: async (listId: string) => { - const response = await this.request(`/api/v1/lists/${listId}/unfavourite`, { method: 'POST' }); + const response = await this.request(`/api/v1/lists/${listId}/unfavourite`, { + method: 'POST', + }); return v.parse(listSchema, response.json); }, @@ -3251,28 +3568,38 @@ class PlApiClient { connect: () => { if (this.#socket) return this.#socket; - const path = buildFullPath('/api/v1/streaming', this.#instance?.configuration.urls.streaming, { access_token: this.accessToken }); + const path = buildFullPath( + '/api/v1/streaming', + this.#instance?.configuration.urls.streaming, + { access_token: this.accessToken }, + ); const ws = new WebSocket(path, this.accessToken as any); let listeners: Array<{ listener: (event: StreamingEvent) => any; stream?: string }> = []; const queue: Array<() => any> = []; - const enqueue = (fn: () => any) => ws.readyState === WebSocket.CONNECTING ? queue.push(fn) : fn(); + const enqueue = (fn: () => any) => + ws.readyState === WebSocket.CONNECTING ? queue.push(fn) : fn(); ws.onmessage = (event) => { const message = v.parse(streamingEventSchema, JSON.parse(event.data as string)); - listeners.filter(({ listener, stream }) => (!stream || message.stream.includes(stream)) && listener(message)); + listeners.filter( + ({ listener, stream }) => + (!stream || message.stream.includes(stream)) && listener(message), + ); }; ws.onopen = () => { - queue.forEach(fn => fn()); + queue.forEach((fn) => fn()); }; this.#socket = { - listen: (listener: (event: StreamingEvent) => any, stream?: string) => listeners.push({ listener, stream }), - unlisten: (listener: (event: StreamingEvent) => any) => listeners = listeners.filter((value) => value.listener !== listener), + listen: (listener: (event: StreamingEvent) => any, stream?: string) => + listeners.push({ listener, stream }), + unlisten: (listener: (event: StreamingEvent) => any) => + (listeners = listeners.filter((value) => value.listener !== listener)), subscribe: (stream: string, { list, tag }: { list?: string; tag?: string } = {}) => enqueue(() => ws.send(JSON.stringify({ type: 'subscribe', stream, list, tag }))), unsubscribe: (stream: string, { list, tag }: { list?: string; tag?: string } = {}) => @@ -3293,20 +3620,32 @@ class PlApiClient { * Notifications concerning the user. This API returns Link headers containing links to the next/previous page. However, the links can also be constructed dynamically using query params and `id` values. * @see {@link https://docs.joinmastodon.org/methods/notifications/#get} */ - getNotifications: async (params?: GetNotificationParams, meta?: RequestMeta) => { + getNotifications: (params?: GetNotificationParams, meta?: RequestMeta) => { const PLEROMA_TYPES = [ - 'chat_mention', 'emoji_reaction', 'report', 'participation_accepted', 'participation_request', 'event_reminder', 'event_update', + 'chat_mention', + 'emoji_reaction', + 'report', + 'participation_accepted', + 'participation_request', + 'event_reminder', + 'event_update', ]; - if (params?.types) params.types = [ - ...params.types, - ...params.types.filter(type => PLEROMA_TYPES.includes(type)).map(type => `pleroma:${type}`), - ]; + if (params?.types) + params.types = [ + ...params.types, + ...params.types + .filter((type) => PLEROMA_TYPES.includes(type)) + .map((type) => `pleroma:${type}`), + ]; - if (params?.exclude_types) params.exclude_types = [ - ...params.exclude_types, - ...params.exclude_types.filter(type => PLEROMA_TYPES.includes(type)).map(type => `pleroma:${type}`), - ]; + if (params?.exclude_types) + params.exclude_types = [ + ...params.exclude_types, + ...params.exclude_types + .filter((type) => PLEROMA_TYPES.includes(type)) + .map((type) => `pleroma:${type}`), + ]; return this.#paginatedGet('/api/v1/notifications', { ...meta, params }, notificationSchema); }, @@ -3328,9 +3667,11 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/notifications/#clear} */ dismissNotifications: async () => { - const response = await this.request('/api/v1/notifications/clear', { method: 'POST' }); + const response = await this.request('/api/v1/notifications/clear', { + method: 'POST', + }); - return response.json as {}; + return response.json; }, /** @@ -3339,9 +3680,14 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/notifications/#dismiss} */ dismissNotification: async (notificationId: string) => { - const response = await this.request(`/api/v1/notifications/${notificationId}/dismiss`, { method: 'POST' }); + const response = await this.request( + `/api/v1/notifications/${notificationId}/dismiss`, + { + method: 'POST', + }, + ); - return response.json as {}; + return response.json; }, /** @@ -3354,9 +3700,12 @@ class PlApiClient { getUnreadNotificationCount: async (params?: GetUnreadNotificationCountParams) => { const response = await this.request('/api/v1/notifications/unread_count', { params }); - return v.parse(v.object({ - count: v.number(), - }), response.json); + return v.parse( + v.object({ + count: v.number(), + }), + response.json, + ); }, /** @@ -3380,7 +3729,10 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/notifications/#update-the-filtering-policy-for-notifications} */ updateNotificationPolicy: async (params: UpdateNotificationPolicyRequest) => { - const response = await this.request('/api/v2/notifications/policy', { method: 'PATCH', body: params }); + const response = await this.request('/api/v2/notifications/policy', { + method: 'PATCH', + body: params, + }); return v.parse(notificationPolicySchema, response.json); }, @@ -3390,7 +3742,7 @@ class PlApiClient { * Notification requests for notifications filtered by the user’s policy. This API returns Link headers containing links to the next/previous page. * @see {@link https://docs.joinmastodon.org/methods/notifications/#get-requests} */ - getNotificationRequests: async (params?: GetNotificationRequestsParams) => + getNotificationRequests: (params?: GetNotificationRequestsParams) => this.#paginatedGet('/api/v1/notifications/requests', { params }, notificationRequestSchema), /** @@ -3399,7 +3751,9 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/notifications/#get-one-request} */ getNotificationRequest: async (notificationRequestId: string) => { - const response = await this.request(`/api/v1/notifications/requests/${notificationRequestId}`); + const response = await this.request( + `/api/v1/notifications/requests/${notificationRequestId}`, + ); return v.parse(notificationRequestSchema, response.json); }, @@ -3410,9 +3764,12 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/notifications/#accept-request} */ acceptNotificationRequest: async (notificationRequestId: string) => { - const response = await this.request(`/api/v1/notifications/requests/${notificationRequestId}/dismiss`, { method: 'POST' }); + const response = await this.request( + `/api/v1/notifications/requests/${notificationRequestId}/dismiss`, + { method: 'POST' }, + ); - return response.json as {}; + return response.json; }, /** @@ -3421,9 +3778,12 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/notifications/#dismiss-request} */ dismissNotificationRequest: async (notificationRequestId: string) => { - const response = await this.request(`/api/v1/notifications/requests/${notificationRequestId}/dismiss`, { method: 'POST' }); + const response = await this.request( + `/api/v1/notifications/requests/${notificationRequestId}/dismiss`, + { method: 'POST' }, + ); - return response.json as {}; + return response.json; }, /** @@ -3433,9 +3793,12 @@ class PlApiClient { * Requires features{@link Features.notificationsRequestsAcceptMultiple}. */ acceptMultipleNotificationRequests: async (notificationRequestIds: Array) => { - const response = await this.request('/api/v1/notifications/requests/accept', { method: 'POST', body: { id: notificationRequestIds } }); + const response = await this.request('/api/v1/notifications/requests/accept', { + method: 'POST', + body: { id: notificationRequestIds }, + }); - return response.json as {}; + return response.json; }, /** @@ -3445,9 +3808,12 @@ class PlApiClient { * Requires features{@link Features.notificationsRequestsAcceptMultiple}. */ dismissMultipleNotificationRequests: async (notificationRequestIds: Array) => { - const response = await this.request('/api/v1/notifications/requests/dismiss', { method: 'POST', body: { id: notificationRequestIds } }); + const response = await this.request('/api/v1/notifications/requests/dismiss', { + method: 'POST', + body: { id: notificationRequestIds }, + }); - return response.json as {}; + return response.json; }, /** @@ -3459,9 +3825,12 @@ class PlApiClient { checkNotificationRequestsMerged: async () => { const response = await this.request('/api/v1/notifications/requests/merged'); - return v.parse(v.object({ - merged: v.boolean(), - }), response.json); + return v.parse( + v.object({ + merged: v.boolean(), + }), + response.json, + ); }, /** @@ -3471,12 +3840,12 @@ class PlApiClient { * @see {@link https://docs.pleroma.social/backend/development/API/differences_in_mastoapi_responses/#delete-apiv1notificationsdestroy_multiple} */ dismissMultipleNotifications: async (notificationIds: string[]) => { - const response = await this.request('/api/v1/notifications/destroy_multiple', { + const response = await this.request('/api/v1/notifications/destroy_multiple', { params: { ids: notificationIds }, method: 'DELETE', }); - return response.json as {}; + return response.json; }, }; @@ -3493,14 +3862,28 @@ class PlApiClient { */ getGroupedNotifications: async (params: GetGroupedNotificationsParams, meta?: RequestMeta) => { if (this.features.groupedNotifications) { - return this.#paginatedGet('/api/v2/notifications', { ...meta, params }, groupedNotificationsResultsSchema, false); - } else { - const response = await this.notifications.getNotifications( - pick(params, ['max_id', 'since_id', 'limit', 'min_id', 'types', 'exclude_types', 'account_id', 'include_filtered']), + return this.#paginatedGet( + '/api/v2/notifications', + { ...meta, params }, + groupedNotificationsResultsSchema, + false, ); - - return this.#groupNotifications(response, params); } + + const response = await this.notifications.getNotifications( + pick(params, [ + 'max_id', + 'since_id', + 'limit', + 'min_id', + 'types', + 'exclude_types', + 'account_id', + 'include_filtered', + ]), + ); + + return this.#groupNotifications(response, params); }, /** @@ -3515,16 +3898,16 @@ class PlApiClient { const response = await this.request(`/api/v2/notifications/${groupKey}`); return v.parse(groupedNotificationsResultsSchema, response.json); - } else { - const response = await this.request(`/api/v1/notifications/${groupKey}`); - - return this.#groupNotifications({ - previous: null, - next: null, - items: [response.json], - partial: false, - }).items; } + + const response = await this.request(`/api/v1/notifications/${groupKey}`); + + return this.#groupNotifications({ + previous: null, + next: null, + items: [response.json], + partial: false, + }).items; }, /** @@ -3536,12 +3919,17 @@ class PlApiClient { */ dismissNotificationGroup: async (groupKey: string) => { if (this.features.groupedNotifications) { - const response = await this.request(`/api/v2/notifications/${groupKey}/dismiss`, { method: 'POST' }); + const response = await this.request( + `/api/v2/notifications/${groupKey}/dismiss`, + { + method: 'POST', + }, + ); - return response.json as {}; - } else { - return this.notifications.dismissNotification(groupKey); + return response.json; } + + return this.notifications.dismissNotification(groupKey); }, /** @@ -3555,9 +3943,9 @@ class PlApiClient { const response = await this.request(`/api/v2/notifications/${groupKey}/accounts`); return v.parse(filteredArray(accountSchema), response.json); - } else { - return (await (this.groupedNotifications.getNotificationGroup(groupKey))).accounts; } + + return (await this.groupedNotifications.getNotificationGroup(groupKey)).accounts; }, /** @@ -3571,14 +3959,25 @@ class PlApiClient { if (this.features.groupedNotifications) { const response = await this.request('/api/v2/notifications/unread_count', { params }); - return v.parse(v.object({ - count: v.number(), - }), response.json); - } else { - return this.notifications.getUnreadNotificationCount( - pick(params || {}, ['max_id', 'since_id', 'limit', 'min_id', 'types', 'exclude_types', 'account_id']), + return v.parse( + v.object({ + count: v.number(), + }), + response.json, ); } + + return this.notifications.getUnreadNotificationCount( + pick(params || {}, [ + 'max_id', + 'since_id', + 'limit', + 'min_id', + 'types', + 'exclude_types', + 'account_id', + ]), + ); }, }; @@ -3589,7 +3988,10 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/push/#create} */ createSubscription: async (params: CreatePushNotificationsSubscriptionParams) => { - const response = await this.request('/api/v1/push/subscription', { method: 'POST', body: params }); + const response = await this.request('/api/v1/push/subscription', { + method: 'POST', + body: params, + }); return v.parse(webPushSubscriptionSchema, response.json); }, @@ -3611,7 +4013,10 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/push/#update} */ updateSubscription: async (params: UpdatePushNotificationsSubscriptionParams) => { - const response = await this.request('/api/v1/push/subscription', { method: 'PUT', body: params }); + const response = await this.request('/api/v1/push/subscription', { + method: 'PUT', + body: params, + }); return v.parse(webPushSubscriptionSchema, response.json); }, @@ -3622,9 +4027,11 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/push/#delete} */ deleteSubscription: async () => { - const response = await this.request('/api/v1/push/subscription', { method: 'DELETE' }); + const response = await this.request('/api/v1/push/subscription', { + method: 'DELETE', + }); - return response.json as {}; + return response.json; }, }; @@ -3660,7 +4067,10 @@ class PlApiClient { * @see {@link https://github.com/mkljczk/pl/blob/fork/docs/development/API/pleroma_api.md#apiv1pleromasearchlocation} */ searchLocation: async (q: string, meta?: RequestMeta) => { - const response = await this.request('/api/v1/pleroma/search/location', { ...meta, params: { q } }); + const response = await this.request('/api/v1/pleroma/search/location', { + ...meta, + params: { q }, + }); return v.parse(filteredArray(locationSchema), response.json); }, @@ -3705,12 +4115,17 @@ class PlApiClient { getInstanceActivity: async () => { const response = await this.request('/api/v1/instance/activity'); - return v.parse(v.array(v.object({ - week: v.string(), - statuses: v.pipe(v.unknown(), v.transform(String)), - logins: v.pipe(v.unknown(), v.transform(String)), - registrations: v.pipe(v.unknown(), v.transform(String)), - })), response.json); + return v.parse( + v.array( + v.object({ + week: v.string(), + statuses: v.pipe(v.unknown(), v.transform(String)), + logins: v.pipe(v.unknown(), v.transform(String)), + registrations: v.pipe(v.unknown(), v.transform(String)), + }), + ), + response.json, + ); }, /** @@ -3758,10 +4173,15 @@ class PlApiClient { target: Array<{ code: string; name: string }>; }>('/api/v1/akkoma/translation/languages'); - return Object.fromEntries(response.json.source.map(source => [ - source.code.toLocaleLowerCase(), - response.json.target.map(lang => lang.code).filter(lang => lang !== source.code).map(lang => lang.toLocaleLowerCase()), - ])); + return Object.fromEntries( + response.json.source.map((source) => [ + source.code.toLocaleLowerCase(), + response.json.target + .map((lang) => lang.code) + .filter((lang) => lang !== source.code) + .map((lang) => lang.toLocaleLowerCase()), + ]), + ); } const response = await this.request('/api/v1/instance/translation_languages'); @@ -3803,7 +4223,8 @@ class PlApiClient { switch (this.features.version.software) { case MITRA: - response = (await this.request('/api/v1/accounts/verify_credentials')).json?.client_config; + response = (await this.request('/api/v1/accounts/verify_credentials')).json + ?.client_config; break; default: response = (await this.request('/api/pleroma/frontend_configurations')).json; @@ -3835,10 +4256,10 @@ class PlApiClient { }, /** - * View a specific version of the terms of service - * Obtain the contents of this server's terms of service, for a specified date, if configured. - * @see {@link https://docs.joinmastodon.org/methods/instance/terms_of_service_date} - */ + * View a specific version of the terms of service + * Obtain the contents of this server's terms of service, for a specified date, if configured. + * @see {@link https://docs.joinmastodon.org/methods/instance/terms_of_service_date} + */ getInstanceTermsOfServiceForDate: async (date: string) => { const response = await this.request(`/api/v1/instance/terms_of_service/${date}`); @@ -3854,7 +4275,9 @@ class PlApiClient { */ getTrendingTags: async (params?: GetTrendingTags) => { const response = await this.request( - this.features.version.software === PIXELFED ? '/api/v1.1/discover/posts/hashtags' : '/api/v1/trends/tags', + this.features.version.software === PIXELFED + ? '/api/v1.1/discover/posts/hashtags' + : '/api/v1/trends/tags', { params }, ); @@ -3868,7 +4291,9 @@ class PlApiClient { */ getTrendingStatuses: async (params?: GetTrendingStatuses) => { const response = await this.request( - this.features.version.software === PIXELFED ? '/api/pixelfed/v2/discover/posts/trending' : '/api/v1/trends/statuses', + this.features.version.software === PIXELFED + ? '/api/pixelfed/v2/discover/posts/trending' + : '/api/v1/trends/statuses', { params }, ); @@ -3905,9 +4330,11 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/announcements/#dismiss} */ dismissAnnouncements: async (announcementId: string) => { - const response = await this.request(`/api/v1/announcements/${announcementId}`, { method: 'POST' }); + const response = await this.request(`/api/v1/announcements/${announcementId}`, { + method: 'POST', + }); - return response.json as {}; + return response.json; }, /** @@ -3916,9 +4343,12 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/announcements/#put-reactions} */ addAnnouncementReaction: async (announcementId: string, emoji: string) => { - const response = await this.request(`/api/v1/announcements/${announcementId}/reactions/${emoji}`, { method: 'PUT' }); + const response = await this.request( + `/api/v1/announcements/${announcementId}/reactions/${emoji}`, + { method: 'PUT' }, + ); - return response.json as {}; + return response.json; }, /** @@ -3927,9 +4357,12 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/announcements/#delete-reactions} */ deleteAnnouncementReaction: async (announcementId: string, emoji: string) => { - const response = await this.request(`/api/v1/announcements/${announcementId}/reactions/${emoji}`, { method: 'DELETE' }); + const response = await this.request( + `/api/v1/announcements/${announcementId}/reactions/${emoji}`, + { method: 'DELETE' }, + ); - return response.json as {}; + return response.json; }, }; @@ -3954,26 +4387,32 @@ class PlApiClient { * View all accounts, optionally matching certain criteria for filtering, up to 100 at a time. * @see {@link https://docs.joinmastodon.org/methods/admin/accounts/#v2} */ - getAccounts: async (params?: AdminGetAccountsParams) => { + getAccounts: (params?: AdminGetAccountsParams) => { if (this.features.mastodonAdminV2) { return this.#paginatedGet('/api/v2/admin/accounts', { params }, adminAccountSchema); - } else { - return this.#paginatedPleromaAccounts(params ? { - query: params.username, - name: params.display_name, - email: params.email, - filters: [ - params.origin === 'local' && 'local', - params.origin === 'remote' && 'external', - params.status === 'active' && 'active', - params.status === 'pending' && 'need_approval', - params.status === 'disabled' && 'deactivated', - params.permissions === 'staff' && 'is_admin', - params.permissions === 'staff' && 'is_moderator', - ].filter(filter => filter).join(','), - page_size: 100, - } : { page_size: 100 }); } + + return this.#paginatedPleromaAccounts( + params + ? { + query: params.username, + name: params.display_name, + email: params.email, + filters: [ + params.origin === 'local' && 'local', + params.origin === 'remote' && 'external', + params.status === 'active' && 'active', + params.status === 'pending' && 'need_approval', + params.status === 'disabled' && 'deactivated', + params.permissions === 'staff' && 'is_admin', + params.permissions === 'staff' && 'is_moderator', + ] + .filter((filter) => filter) + .join(','), + page_size: 100, + } + : { page_size: 100 }, + ); }, /** @@ -4002,11 +4441,16 @@ class PlApiClient { let response; if (this.features.mastodonAdmin) { - response = await this.request(`/api/v1/admin/accounts/${accountId}/approve`, { method: 'POST' }); + response = await this.request(`/api/v1/admin/accounts/${accountId}/approve`, { + method: 'POST', + }); } else { const account = await this.admin.accounts.getAccount(accountId)!; - response = await this.request('/api/v1/pleroma/admin/users/approve', { method: 'PATCH', body: { nicknames: [account.username] } }); + response = await this.request('/api/v1/pleroma/admin/users/approve', { + method: 'PATCH', + body: { nicknames: [account.username] }, + }); response.json = response.json?.users?.[0]; } @@ -4022,13 +4466,18 @@ class PlApiClient { let response; if (this.features.mastodonAdmin) { - response = await this.request(`/api/v1/admin/accounts/${accountId}/reject`, { method: 'POST' }); + response = await this.request(`/api/v1/admin/accounts/${accountId}/reject`, { + method: 'POST', + }); } else { const account = await this.admin.accounts.getAccount(accountId)!; - response = await this.request('/api/v1/pleroma/admin/users', { method: 'DELETE', body: { - nicknames: [account.username], - } }); + response = await this.request('/api/v1/pleroma/admin/users', { + method: 'DELETE', + body: { + nicknames: [account.username], + }, + }); } return v.safeParse(adminAccountSchema, response.json).output || {}; @@ -4038,12 +4487,18 @@ class PlApiClient { * Requires features{@link Features.pleromaAdminAccounts}. */ createAccount: async (params: AdminCreateAccountParams) => { - const response = await this.request('/api/v1/pleroma/admin/users', { method: 'POST', body: { users: [params] } }); + const response = await this.request('/api/v1/pleroma/admin/users', { + method: 'POST', + body: { users: [params] }, + }); - return v.parse(v.object({ - nickname: v.string(), - email: v.string(), - }), response.json[0]?.data); + return v.parse( + v.object({ + nickname: v.string(), + email: v.string(), + }), + response.json[0]?.data, + ); }, /** @@ -4055,13 +4510,18 @@ class PlApiClient { let response; if (this.features.mastodonAdmin || this.features.version.software === MITRA) { - response = await this.request(`/api/v1/admin/accounts/${accountId}`, { method: 'DELETE' }); + response = await this.request(`/api/v1/admin/accounts/${accountId}`, { + method: 'DELETE', + }); } else { const account = await this.admin.accounts.getAccount(accountId)!; - response = await this.request('/api/v1/pleroma/admin/users', { method: 'DELETE', body: { - nicknames: [account.username], - } }); + response = await this.request('/api/v1/pleroma/admin/users', { + method: 'DELETE', + body: { + nicknames: [account.username], + }, + }); } return v.safeParse(adminAccountSchema, response.json).output || {}; @@ -4072,18 +4532,26 @@ class PlApiClient { * Perform an action against an account and log this action in the moderation history. Also resolves any open reports against this account. * @see {@link https://docs.joinmastodon.org/methods/admin/accounts/#action} */ - performAccountAction: async (accountId: string, type: AdminAccountAction, params?: AdminPerformAccountActionParams) => { + performAccountAction: async ( + accountId: string, + type: AdminAccountAction, + params?: AdminPerformAccountActionParams, + ) => { let response; if (this.features.mastodonAdmin) { - response = await this.request(`/api/v1/admin/accounts/${accountId}/action`, { body: { ...params, type } }); + response = await this.request(`/api/v1/admin/accounts/${accountId}/action`, { + body: { ...params, type }, + }); } else { const account = await this.admin.accounts.getAccount(accountId)!; switch (type) { case 'disable': case 'suspend': - response = await this.request('/api/v1/pleroma/admin/users/deactivate', { body: { nicknames: [account.username] } }); + response = await this.request('/api/v1/pleroma/admin/users/deactivate', { + body: { nicknames: [account.username] }, + }); break; default: response = { json: {} }; @@ -4092,7 +4560,7 @@ class PlApiClient { if (params?.report_id) await this.admin.reports.resolveReport(params.report_id); } - return response.json as {}; + return response.json; }, /** @@ -4104,10 +4572,15 @@ class PlApiClient { let response; if (this.features.mastodonAdmin) { - response = await this.request(`/api/v1/admin/accounts/${accountId}/enable`, { method: 'POST' }); + response = await this.request(`/api/v1/admin/accounts/${accountId}/enable`, { + method: 'POST', + }); } else { const account = await this.admin.accounts.getAccount(accountId)!; - response = await this.request('/api/v1/pleroma/admin/users/activate', { method: 'PATCH', body: { nicknames: [account.username] } }); + response = await this.request('/api/v1/pleroma/admin/users/activate', { + method: 'PATCH', + body: { nicknames: [account.username] }, + }); response.json = response.json?.users?.[0]; } @@ -4120,7 +4593,9 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/admin/accounts/#unsilence} */ unsilenceAccount: async (accountId: string) => { - const response = await this.request(`/api/v1/admin/accounts/${accountId}/unsilence`, { method: 'POST' }); + const response = await this.request(`/api/v1/admin/accounts/${accountId}/unsilence`, { + method: 'POST', + }); return v.parse(adminAccountSchema, response.json); }, @@ -4134,11 +4609,16 @@ class PlApiClient { let response; if (this.features.mastodonAdmin) { - response = await this.request(`/api/v1/admin/accounts/${accountId}/unsuspend`, { method: 'POST' }); + response = await this.request(`/api/v1/admin/accounts/${accountId}/unsuspend`, { + method: 'POST', + }); } else { const { account } = await this.admin.accounts.getAccount(accountId)!; - response = await this.request('/api/v1/pleroma/admin/users/activate', { method: 'PATCH', body: { nicknames: [account!.acct] } }); + response = await this.request('/api/v1/pleroma/admin/users/activate', { + method: 'PATCH', + body: { nicknames: [account!.acct] }, + }); response.json = response.json?.users?.[0]; } @@ -4151,7 +4631,9 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/admin/accounts/#unsensitive} */ unsensitiveAccount: async (accountId: string) => { - const response = await this.request(`/api/v1/admin/accounts/${accountId}/unsensitive`, { method: 'POST' }); + const response = await this.request(`/api/v1/admin/accounts/${accountId}/unsensitive`, { + method: 'POST', + }); return v.parse(adminAccountSchema, response.json); }, @@ -4162,16 +4644,19 @@ class PlApiClient { promoteToAdmin: async (accountId: string) => { const { account } = await this.admin.accounts.getAccount(accountId)!; - await this.request('/api/v1/pleroma/admin/users/permission_group/moderator', { + await this.request('/api/v1/pleroma/admin/users/permission_group/moderator', { method: 'DELETE', body: { nicknames: [account!.acct] }, }); - const response = await this.request('/api/v1/pleroma/admin/users/permission_group/admin', { - method: 'POST', - body: { nicknames: [account!.acct] }, - }); + const response = await this.request( + '/api/v1/pleroma/admin/users/permission_group/admin', + { + method: 'POST', + body: { nicknames: [account!.acct] }, + }, + ); - return response.json as {}; + return response.json; }, /** @@ -4180,12 +4665,19 @@ class PlApiClient { promoteToModerator: async (accountId: string) => { const { account } = await this.admin.accounts.getAccount(accountId)!; - await this.request('/api/v1/pleroma/admin/users/permission_group/admin', { - method: 'DELETE', body: { nicknames: [account!.acct] } }); - const response = await this.request('/api/v1/pleroma/admin/users/permission_group/moderator', { - method: 'POST', body: { nicknames: [account!.acct] } }); + await this.request('/api/v1/pleroma/admin/users/permission_group/admin', { + method: 'DELETE', + body: { nicknames: [account!.acct] }, + }); + const response = await this.request( + '/api/v1/pleroma/admin/users/permission_group/moderator', + { + method: 'POST', + body: { nicknames: [account!.acct] }, + }, + ); - return response.json as {}; + return response.json; }, /** @@ -4194,16 +4686,19 @@ class PlApiClient { demoteToUser: async (accountId: string) => { const { account } = await this.admin.accounts.getAccount(accountId)!; - await this.request('/api/v1/pleroma/admin/users/permission_group/moderator', { - method: 'DELETE', - body: { nicknames: [account!.acct] }, - }); - const response = await this.request('/api/v1/pleroma/admin/users/permission_group/admin', { + await this.request('/api/v1/pleroma/admin/users/permission_group/moderator', { method: 'DELETE', body: { nicknames: [account!.acct] }, }); + const response = await this.request( + '/api/v1/pleroma/admin/users/permission_group/admin', + { + method: 'DELETE', + body: { nicknames: [account!.acct] }, + }, + ); - return response.json as {}; + return response.json; }, /** @@ -4215,12 +4710,12 @@ class PlApiClient { suggestUser: async (accountId: string) => { const { account } = await this.admin.accounts.getAccount(accountId)!; - const response = await this.request('/api/v1/pleroma/admin/users/suggest', { + const response = await this.request('/api/v1/pleroma/admin/users/suggest', { method: 'PATCH', body: { nicknames: [account!.acct] }, }); - return response.json as {}; + return response.json; }, /** @@ -4232,12 +4727,12 @@ class PlApiClient { unsuggestUser: async (accountId: string) => { const { account } = await this.admin.accounts.getAccount(accountId)!; - const response = await this.request('/api/v1/pleroma/admin/users/unsuggest', { + const response = await this.request('/api/v1/pleroma/admin/users/unsuggest', { method: 'PATCH', body: { nicknames: [account!.acct] }, }); - return response.json as {}; + return response.json; }, /** @@ -4249,12 +4744,12 @@ class PlApiClient { tagUser: async (accountId: string, tags: Array) => { const { account } = await this.admin.accounts.getAccount(accountId)!; - const response = await this.request('/api/v1/pleroma/admin/users/tag', { + const response = await this.request('/api/v1/pleroma/admin/users/tag', { method: 'PUT', body: { nicknames: [account!.acct], tags }, }); - return response.json as {}; + return response.json; }, /** @@ -4266,12 +4761,12 @@ class PlApiClient { untagUser: async (accountId: string, tags: Array) => { const { account } = await this.admin.accounts.getAccount(accountId)!; - const response = await this.request('/api/v1/pleroma/admin/users/tag', { + const response = await this.request('/api/v1/pleroma/admin/users/tag', { method: 'DELETE', body: { nicknames: [account!.acct], tags }, }); - return response.json as {}; + return response.json; }, }, @@ -4330,11 +4825,14 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/admin/domain_blocks/#delete} */ deleteDomainBlock: async (domainBlockId: string) => { - const response = await this.request(`/api/v1/admin/domain_blocks/${domainBlockId}`, { - method: 'DELETE', - }); + const response = await this.request( + `/api/v1/admin/domain_blocks/${domainBlockId}`, + { + method: 'DELETE', + }, + ); - return response.json as {}; + return response.json; }, }, @@ -4345,19 +4843,23 @@ class PlApiClient { * View information about all reports. * @see {@link https://docs.joinmastodon.org/methods/admin/reports/#get} */ - getReports: async (params?: AdminGetReportsParams) => { + getReports: (params?: AdminGetReportsParams) => { if (this.features.mastodonAdmin) { - if (params?.resolved === undefined && (this.features.version.software === GOTOSOCIAL || this.features.version.software === PLEROMA)) { + if ( + params?.resolved === undefined && + (this.features.version.software === GOTOSOCIAL || + this.features.version.software === PLEROMA) + ) { if (!params) params = {}; params.resolved = false; } return this.#paginatedGet('/api/v1/admin/reports', { params }, adminReportSchema); - } else { - return this.#paginatedPleromaReports({ - state: params?.resolved === true ? 'resolved' : 'open', - page_size: params?.limit || 100, - }); } + + return this.#paginatedPleromaReports({ + state: params?.resolved === true ? 'resolved' : 'open', + page_size: params?.limit || 100, + }); }, /** @@ -4381,7 +4883,10 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/admin/reports/#update} */ updateReport: async (reportId: string, params: AdminUpdateReportParams) => { - const response = await this.request(`/api/v1/admin/reports/${reportId}`, { method: 'PUT', body: params }); + const response = await this.request(`/api/v1/admin/reports/${reportId}`, { + method: 'PUT', + body: params, + }); return v.parse(adminReportSchema, response.json); }, @@ -4392,7 +4897,9 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/admin/reports/#assign_to_self} */ assignReportToSelf: async (reportId: string) => { - const response = await this.request(`/api/v1/admin/reports/${reportId}/assign_to_self`, { method: 'POST' }); + const response = await this.request(`/api/v1/admin/reports/${reportId}/assign_to_self`, { + method: 'POST', + }); return v.parse(adminReportSchema, response.json); }, @@ -4403,7 +4910,9 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/admin/reports/#unassign} */ unassignReport: async (reportId: string) => { - const response = await this.request(`/api/v1/admin/reports/${reportId}/unassign`, { method: 'POST' }); + const response = await this.request(`/api/v1/admin/reports/${reportId}/unassign`, { + method: 'POST', + }); return v.parse(adminReportSchema, response.json); }, @@ -4420,7 +4929,10 @@ class PlApiClient { resolveReport: async (reportId: string, action_taken_comment?: string) => { let response; if (this.features.mastodonAdmin) { - response = await this.request(`/api/v1/admin/reports/${reportId}/resolve`, { method: 'POST', body: { action_taken_comment } }); + response = await this.request(`/api/v1/admin/reports/${reportId}/resolve`, { + method: 'POST', + body: { action_taken_comment }, + }); } else { response = await this.request(`/api/v1/pleroma/admin/reports/${reportId}`, { method: 'PATCH', @@ -4439,7 +4951,9 @@ class PlApiClient { reopenReport: async (reportId: string) => { let response; if (this.features.mastodonAdmin) { - response = await this.request(`/api/v1/admin/reports/${reportId}/reopen`, { method: 'POST' }); + response = await this.request(`/api/v1/admin/reports/${reportId}/reopen`, { + method: 'POST', + }); } else { response = await this.request(`/api/v1/pleroma/admin/reports/${reportId}`, { method: 'PATCH', @@ -4460,20 +4974,21 @@ class PlApiClient { * Requires features{@link Features.pleromaAdminStatuses}. * @see {@link https://docs.pleroma.social/backend/development/API/admin_api/#get-apiv1pleromaadminstatuses} */ - getStatuses: async (params?: AdminGetStatusesParams) => this.#paginatedPleromaStatuses({ - page_size: params?.limit || 100, - page: 1, - local_only: params?.local_only, - with_reblogs: params?.with_reblogs, - godmode: params?.with_private, - }), + getStatuses: (params?: AdminGetStatusesParams) => + this.#paginatedPleromaStatuses({ + page_size: params?.limit || 100, + page: 1, + local_only: params?.local_only, + with_reblogs: params?.with_reblogs, + godmode: params?.with_private, + }), /** * Show status by id * * Requires features{@link Features.pleromaAdminStatuses}. * @see {@link https://docs.pleroma.social/backend/development/API/admin_api/#get-apiv1pleromaadminstatusesid} - */ + */ getStatus: async (statusId: string) => { const response = await this.request(`/api/v1/pleroma/admin/statuses/${statusId}`); @@ -4487,7 +5002,10 @@ class PlApiClient { * @see {@link https://docs.pleroma.social/backend/development/API/admin_api/#put-apiv1pleromaadminstatusesid} */ updateStatus: async (statusId: string, params: AdminUpdateStatusParams) => { - const response = await this.request(`/api/v1/pleroma/admin/statuses/${statusId}`, { method: 'PUT', body: params }); + const response = await this.request(`/api/v1/pleroma/admin/statuses/${statusId}`, { + method: 'PUT', + body: params, + }); return v.parse(statusSchema, response.json); }, @@ -4502,19 +5020,29 @@ class PlApiClient { let response; if (this.features.version.software === MITRA) { - response = await this.request(`/api/v1/admin/posts/${statusId}`, { method: 'DELETE' }); + response = await this.request(`/api/v1/admin/posts/${statusId}`, { + method: 'DELETE', + }); } else { - response = await this.request(`/api/v1/pleroma/admin/statuses/${statusId}`, { method: 'DELETE' }); + response = await this.request(`/api/v1/pleroma/admin/statuses/${statusId}`, { + method: 'DELETE', + }); } - return response.json as {}; + return response.json; }, /** * Requires features{@link Features.pleromaAdminStatusesRedact} */ - redactStatus: async (statusId: string, params: EditStatusParams & { overwrite?: boolean }) => { - const response = await this.request(`/api/v1/pleroma/admin/statuses/${statusId}/redact`, { method: 'PATCH', body: params }); + redactStatus: async ( + statusId: string, + params: EditStatusParams & { overwrite?: boolean }, + ) => { + const response = await this.request(`/api/v1/pleroma/admin/statuses/${statusId}/redact`, { + method: 'PATCH', + body: params, + }); return v.parse(statusSchema, response.json); }, @@ -4570,15 +5098,21 @@ class PlApiClient { * List all canonical email blocks * @see {@link https://docs.joinmastodon.org/methods/admin/canonical_email_blocks/#get} */ - getCanonicalEmailBlocks: async (params?: AdminGetCanonicalEmailBlocks) => - this.#paginatedGet('/api/v1/admin/canonical_email_blocks', { params }, adminCanonicalEmailBlockSchema), + getCanonicalEmailBlocks: (params?: AdminGetCanonicalEmailBlocks) => + this.#paginatedGet( + '/api/v1/admin/canonical_email_blocks', + { params }, + adminCanonicalEmailBlockSchema, + ), /** * Show a single canonical email block * @see {@link https://docs.joinmastodon.org/methods/admin/canonical_email_blocks/#get-one} */ getCanonicalEmailBlock: async (canonicalEmailBlockId: string) => { - const response = await this.request(`/api/v1/admin/canonical_email_blocks/${canonicalEmailBlockId}`); + const response = await this.request( + `/api/v1/admin/canonical_email_blocks/${canonicalEmailBlockId}`, + ); return v.parse(adminCanonicalEmailBlockSchema, response.json); }, @@ -4589,7 +5123,10 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/admin/canonical_email_blocks/#test} */ testCanonicalEmailBlock: async (email: string) => { - const response = await this.request('/api/v1/admin/canonical_email_blocks/test', { method: 'POST', body: { email } }); + const response = await this.request('/api/v1/admin/canonical_email_blocks/test', { + method: 'POST', + body: { email }, + }); return v.parse(filteredArray(adminCanonicalEmailBlockSchema), response.json); }, @@ -4599,7 +5136,10 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/admin/canonical_email_blocks/#create} */ createCanonicalEmailBlock: async (email: string, canonical_email_hash?: string) => { - const response = await this.request('/api/v1/admin/canonical_email_blocks', { method: 'POST', body: { email, canonical_email_hash } }); + const response = await this.request('/api/v1/admin/canonical_email_blocks', { + method: 'POST', + body: { email, canonical_email_hash }, + }); return v.parse(filteredArray(adminCanonicalEmailBlockSchema), response.json); }, @@ -4609,9 +5149,12 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/admin/canonical_email_blocks/#delete} */ deleteCanonicalEmailBlock: async (canonicalEmailBlockId: string) => { - const response = await this.request(`/api/v1/admin/canonical_email_blocks/${canonicalEmailBlockId}`, { method: 'DELETE' }); + const response = await this.request( + `/api/v1/admin/canonical_email_blocks/${canonicalEmailBlockId}`, + { method: 'DELETE' }, + ); - return response.json as {}; + return response.json; }, }, @@ -4623,7 +5166,10 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/admin/dimensions/#get} */ getDimensions: async (keys: AdminDimensionKey[], params?: AdminGetDimensionsParams) => { - const response = await this.request('/api/v1/admin/dimensions', { method: 'POST', params: { ...params, keys } }); + const response = await this.request('/api/v1/admin/dimensions', { + method: 'POST', + params: { ...params, keys }, + }); return v.parse(filteredArray(adminDimensionSchema), response.json); }, @@ -4656,7 +5202,10 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/admin/domain_allows/#create} */ createDomainAllow: async (domain: string) => { - const response = await this.request('/api/v1/admin/domain_allows', { method: 'POST', body: { domain } }); + const response = await this.request('/api/v1/admin/domain_allows', { + method: 'POST', + body: { domain }, + }); return v.parse(adminDomainAllowSchema, response.json); }, @@ -4667,9 +5216,14 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/admin/domain_allows/#delete} */ deleteDomainAllow: async (domainAllowId: string) => { - const response = await this.request(`/api/v1/admin/domain_allows/${domainAllowId}`, { method: 'DELETE' }); + const response = await this.request( + `/api/v1/admin/domain_allows/${domainAllowId}`, + { + method: 'DELETE', + }, + ); - return response.json as {}; + return response.json; }, }, @@ -4681,7 +5235,11 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/admin/email_domain_blocks/#get} */ getEmailDomainBlocks: (params?: AdminGetEmailDomainBlocksParams) => - this.#paginatedGet('/api/v1/admin/email_domain_blocks', { params }, adminEmailDomainBlockSchema), + this.#paginatedGet( + '/api/v1/admin/email_domain_blocks', + { params }, + adminEmailDomainBlockSchema, + ), /** * Get a single blocked email domain @@ -4689,7 +5247,9 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/admin/email_domain_blocks/#get-one} */ getEmailDomainBlock: async (emailDomainBlockId: string) => { - const response = await this.request(`/api/v1/admin/email_domain_blocks/${emailDomainBlockId}`); + const response = await this.request( + `/api/v1/admin/email_domain_blocks/${emailDomainBlockId}`, + ); return v.parse(adminEmailDomainBlockSchema, response.json); }, @@ -4700,7 +5260,10 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/admin/email_domain_blocks/#create} */ createEmailDomainBlock: async (domain: string) => { - const response = await this.request('/api/v1/admin/email_domain_blocks', { method: 'POST', body: { domain } }); + const response = await this.request('/api/v1/admin/email_domain_blocks', { + method: 'POST', + body: { domain }, + }); return v.parse(adminEmailDomainBlockSchema, response.json); }, @@ -4711,9 +5274,12 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/admin/email_domain_blocks/#delete} */ deleteEmailDomainBlock: async (emailDomainBlockId: string) => { - const response = await this.request(`/api/v1/admin/email_domain_blocks/${emailDomainBlockId}`, { method: 'DELETE' }); + const response = await this.request( + `/api/v1/admin/email_domain_blocks/${emailDomainBlockId}`, + { method: 'DELETE' }, + ); - return response.json as {}; + return response.json; }, }, @@ -4744,7 +5310,10 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/admin/ip_blocks/#create} */ createIpBlock: async (params: AdminCreateIpBlockParams) => { - const response = await this.request('/api/v1/admin/ip_blocks', { method: 'POST', body: params }); + const response = await this.request('/api/v1/admin/ip_blocks', { + method: 'POST', + body: params, + }); return v.parse(adminIpBlockSchema, response.json); }, @@ -4755,7 +5324,10 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/admin/ip_blocks/#update} */ updateIpBlock: async (ipBlockId: string, params: AdminCreateIpBlockParams) => { - const response = await this.request(`/api/v1/admin/ip_blocks/${ipBlockId}`, { method: 'POST', body: params }); + const response = await this.request(`/api/v1/admin/ip_blocks/${ipBlockId}`, { + method: 'POST', + body: params, + }); return v.parse(adminIpBlockSchema, response.json); }, @@ -4766,9 +5338,11 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/admin/ip_blocks/#delete} */ deleteIpBlock: async (ipBlockId: string) => { - const response = await this.request(`/api/v1/admin/ip_blocks/${ipBlockId}`, { method: 'DELETE' }); + const response = await this.request(`/api/v1/admin/ip_blocks/${ipBlockId}`, { + method: 'DELETE', + }); - return response.json as {}; + return response.json; }, }, @@ -4779,8 +5353,16 @@ class PlApiClient { * Obtain quantitative metrics about the server. * @see {@link https://docs.joinmastodon.org/methods/admin/measures/#get} */ - getMeasures: async (keys: AdminMeasureKey[], start_at: string, end_at: string, params?: AdminGetMeasuresParams) => { - const response = await this.request('/api/v1/admin/measures', { method: 'POST', params: { ...params, keys, start_at, end_at } }); + getMeasures: async ( + keys: AdminMeasureKey[], + start_at: string, + end_at: string, + params?: AdminGetMeasuresParams, + ) => { + const response = await this.request('/api/v1/admin/measures', { + method: 'POST', + params: { ...params, keys, start_at, end_at }, + }); return v.parse(filteredArray(adminMeasureSchema), response.json); }, @@ -4795,7 +5377,10 @@ class PlApiClient { * @see {@link https://docs.joinmastodon.org/methods/admin/retention/#create} */ getRetention: async (start_at: string, end_at: string, frequency: 'day' | 'month') => { - const response = await this.request('/api/v1/admin/retention', { method: 'POST', params: { start_at, end_at, frequency } }); + const response = await this.request('/api/v1/admin/retention', { + method: 'POST', + params: { start_at, end_at, frequency }, + }); return v.parse(filteredArray(adminCohortSchema), response.json); }, @@ -4808,14 +5393,22 @@ class PlApiClient { * Requires features{@link Features.pleromaAdminAnnouncements}. * @see {@link https://docs.pleroma.social/backend/development/API/admin_api/#get-apiv1pleromaadminannouncements} */ - getAnnouncements: async (params?: AdminGetAnnouncementsParams): Promise> => { + getAnnouncements: async ( + params?: AdminGetAnnouncementsParams, + ): Promise> => { const response = await this.request('/api/v1/pleroma/admin/announcements', { params }); const items = v.parse(filteredArray(adminAnnouncementSchema), response.json); return { previous: null, - next: items.length ? () => this.admin.announcements.getAnnouncements({ ...params, offset: (params?.offset || 0) + items.length }) : null, + next: items.length + ? () => + this.admin.announcements.getAnnouncements({ + ...params, + offset: (params?.offset || 0) + items.length, + }) + : null, items, partial: false, }; @@ -4828,7 +5421,9 @@ class PlApiClient { * @see {@link https://docs.pleroma.social/backend/development/API/admin_api/#get-apiv1pleromaadminannouncementsid} */ getAnnouncement: async (announcementId: string) => { - const response = await this.request(`/api/v1/pleroma/admin/announcements/${announcementId}`); + const response = await this.request( + `/api/v1/pleroma/admin/announcements/${announcementId}`, + ); return v.parse(adminAnnouncementSchema, response.json); }, @@ -4840,7 +5435,10 @@ class PlApiClient { * @see {@link https://docs.pleroma.social/backend/development/API/admin_api/#post-apiv1pleromaadminannouncements} */ createAnnouncement: async (params: AdminCreateAnnouncementParams) => { - const response = await this.request('/api/v1/pleroma/admin/announcements', { method: 'POST', body: params }); + const response = await this.request('/api/v1/pleroma/admin/announcements', { + method: 'POST', + body: params, + }); return v.parse(adminAnnouncementSchema, response.json); }, @@ -4852,7 +5450,10 @@ class PlApiClient { * @see {@link https://docs.pleroma.social/backend/development/API/admin_api/#patch-apiv1pleromaadminannouncementsid} */ updateAnnouncement: async (announcementId: string, params: AdminUpdateAnnouncementParams) => { - const response = await this.request(`/api/v1/pleroma/admin/announcements/${announcementId}`, { method: 'PATCH', body: params }); + const response = await this.request( + `/api/v1/pleroma/admin/announcements/${announcementId}`, + { method: 'PATCH', body: params }, + ); return v.parse(adminAnnouncementSchema, response.json); }, @@ -4864,9 +5465,12 @@ class PlApiClient { * @see {@link https://docs.pleroma.social/backend/development/API/admin_api/#delete-apiv1pleromaadminannouncementsid} */ deleteAnnouncement: async (announcementId: string) => { - const response = await this.request(`/api/v1/pleroma/admin/announcements/${announcementId}`, { method: 'DELETE' }); + const response = await this.request( + `/api/v1/pleroma/admin/announcements/${announcementId}`, + { method: 'DELETE' }, + ); - return response.json as {}; + return response.json; }, }, @@ -4888,7 +5492,10 @@ class PlApiClient { * Requires features{@link Features.domains}. */ createDomain: async (params: AdminCreateDomainParams) => { - const response = await this.request('/api/v1/pleroma/admin/domains', { method: 'POST', body: params }); + const response = await this.request('/api/v1/pleroma/admin/domains', { + method: 'POST', + body: params, + }); return v.parse(adminDomainSchema, response.json); }, @@ -4899,7 +5506,10 @@ class PlApiClient { * Requires features{@link Features.domains}. */ updateDomain: async (domainId: string, isPublic: boolean) => { - const response = await this.request(`/api/v1/pleroma/admin/domains/${domainId}`, { method: 'PATCH', body: { public: isPublic } }); + const response = await this.request(`/api/v1/pleroma/admin/domains/${domainId}`, { + method: 'PATCH', + body: { public: isPublic }, + }); return v.parse(adminDomainSchema, response.json); }, @@ -4910,9 +5520,14 @@ class PlApiClient { * Requires features{@link Features.domains}. */ deleteDomain: async (domainId: string) => { - const response = await this.request(`/api/v1/pleroma/admin/domains/${domainId}`, { method: 'DELETE' }); + const response = await this.request( + `/api/v1/pleroma/admin/domains/${domainId}`, + { + method: 'DELETE', + }, + ); - return response.json as {}; + return response.json; }, }, @@ -4922,15 +5537,30 @@ class PlApiClient { * * Requires features{@link Features.pleromaAdminModerationLog}. * @see {@link https://docs.pleroma.social/backend/development/API/admin_api/#get-apiv1pleromaadminmoderation_log} - */ - getModerationLog: async ({ limit, ...params }: AdminGetModerationLogParams = {}): Promise> => { - const response = await this.request('/api/v1/pleroma/admin/moderation_log', { params: { page_size: limit, ...params } }); + */ + getModerationLog: async ({ limit, ...params }: AdminGetModerationLogParams = {}): Promise< + PaginatedResponse + > => { + const response = await this.request('/api/v1/pleroma/admin/moderation_log', { + params: { page_size: limit, ...params }, + }); const items = v.parse(filteredArray(adminModerationLogEntrySchema), response.json.items); return { - previous: (params.page && params.page > 1) ? () => this.admin.moderationLog.getModerationLog({ ...params, page: params.page! - 1 }) : null, - next: response.json.total > (params.page || 1) * (limit || 50) ? () => this.admin.moderationLog.getModerationLog({ ...params, page: (params.page || 1) + 1 }) : null, + previous: + params.page && params.page > 1 + ? () => + this.admin.moderationLog.getModerationLog({ ...params, page: params.page! - 1 }) + : null, + next: + response.json.total > (params.page || 1) * (limit || 50) + ? () => + this.admin.moderationLog.getModerationLog({ + ...params, + page: (params.page || 1) + 1, + }) + : null, items, partial: response.status === 206, }; @@ -4957,7 +5587,10 @@ class PlApiClient { * @see {@link https://docs.pleroma.social/backend/development/API/admin_api/#post-apiv1pleromaadminrelay} */ followRelay: async (relayUrl: string) => { - const response = await this.request('/api/v1/pleroma/admin/relay', { method: 'POST', body: { relay_url: relayUrl } }); + const response = await this.request('/api/v1/pleroma/admin/relay', { + method: 'POST', + body: { relay_url: relayUrl }, + }); return v.parse(adminRelaySchema, response.json); }, @@ -4969,7 +5602,10 @@ class PlApiClient { * @see {@link https://docs.pleroma.social/backend/development/API/admin_api/#delete-apiv1pleromaadminrelay} */ unfollowRelay: async (relayUrl: string, force = false) => { - const response = await this.request('/api/v1/pleroma/admin/relay', { method: 'DELETE', body: { relay_url: relayUrl, force } }); + const response = await this.request('/api/v1/pleroma/admin/relay', { + method: 'DELETE', + body: { relay_url: relayUrl, force }, + }); return v.parse(adminRelaySchema, response.json); }, @@ -4983,7 +5619,11 @@ class PlApiClient { * @see {@link https://docs.pleroma.social/backend/development/API/admin_api/#get-apiv1pleromaadminrules} */ getRules: async () => { - const response = await this.request(this.features.version.software === GOTOSOCIAL ? '/api/v1/admin/instance/rules' : '/api/v1/pleroma/admin/rules'); + const response = await this.request( + this.features.version.software === GOTOSOCIAL + ? '/api/v1/admin/instance/rules' + : '/api/v1/pleroma/admin/rules', + ); return v.parse(filteredArray(adminRuleSchema), response.json); }, @@ -4996,7 +5636,9 @@ class PlApiClient { */ createRule: async (params: AdminCreateRuleParams) => { const response = await this.request( - this.features.version.software === GOTOSOCIAL ? '/api/v1/admin/instance/rules' : '/api/v1/pleroma/admin/rules', + this.features.version.software === GOTOSOCIAL + ? '/api/v1/admin/instance/rules' + : '/api/v1/pleroma/admin/rules', { method: 'POST', body: params }, ); @@ -5025,12 +5667,12 @@ class PlApiClient { * @see {@link https://docs.pleroma.social/backend/development/API/admin_api/#delete-apiv1pleromaadminrulesid} */ deleteRule: async (ruleId: string) => { - const response = await this.request( + const response = await this.request( `/api/v1/${this.features.version.software === GOTOSOCIAL ? 'admin/instance' : 'pleroma/admin'}/rules/${ruleId}`, { method: 'DELETE' }, ); - return response.json as {}; + return response.json; }, }, @@ -5042,7 +5684,10 @@ class PlApiClient { }, updatePleromaConfig: async (params: PleromaConfig['configs']) => { - const response = await this.request('/api/v1/pleroma/admin/config', { method: 'POST', body: { configs: params } }); + const response = await this.request('/api/v1/pleroma/admin/config', { + method: 'POST', + body: { configs: params }, + }); return v.parse(pleromaConfigSchema, response.json); }, @@ -5077,19 +5722,21 @@ class PlApiClient { * @see {@link https://docs.gotosocial.org/en/latest/api/swagger/} */ createCustomEmoji: async (params: AdminCreateCustomEmojiParams) => { - const response = await this.request( - '/api/v1/admin/custom_emojis', - { method: 'POST', body: params, contentType: '' }, - ); + const response = await this.request('/api/v1/admin/custom_emojis', { + method: 'POST', + body: params, + contentType: '', + }); return v.parse(adminCustomEmojiSchema, response.json); }, updateCustomEmoji: async (emojiId: string, params: AdminUpdateCustomEmojiParams) => { - const response = await this.request( - `/api/v1/admin/custom_emojis/${emojiId}`, - { method: 'PATCH', body: params, contentType: '' }, - ); + const response = await this.request(`/api/v1/admin/custom_emojis/${emojiId}`, { + method: 'PATCH', + body: params, + contentType: '', + }); return v.parse(adminCustomEmojiSchema, response.json); }, @@ -5101,10 +5748,9 @@ class PlApiClient { * @see {@link https://docs.gotosocial.org/en/latest/api/swagger/} */ deleteCustomEmoji: async (emojiId: string) => { - const response = await this.request( - `/api/v1/admin/custom_emojis/${emojiId}`, - { method: 'DELETE' }, - ); + const response = await this.request(`/api/v1/admin/custom_emojis/${emojiId}`, { + method: 'DELETE', + }); return v.parse(adminCustomEmojiSchema, response.json); }, @@ -5119,18 +5765,21 @@ class PlApiClient { getOembed: async (url: string, maxwidth?: number, maxheight?: number) => { const response = await this.request('/api/oembed', { params: { url, maxwidth, maxheight } }); - return v.parse(v.object({ - type: v.fallback(v.string(), 'rich'), - version: v.fallback(v.string(), ''), - author_name: v.fallback(v.string(), ''), - author_url: v.fallback(v.string(), ''), - provider_name: v.fallback(v.string(), ''), - provider_url: v.fallback(v.string(), ''), - cache_age: v.number(), - html: v.string(), - width: v.fallback(v.nullable(v.number()), null), - height: v.fallback(v.nullable(v.number()), null), - }), response.json); + return v.parse( + v.object({ + type: v.fallback(v.string(), 'rich'), + version: v.fallback(v.string(), ''), + author_name: v.fallback(v.string(), ''), + author_url: v.fallback(v.string(), ''), + provider_name: v.fallback(v.string(), ''), + provider_url: v.fallback(v.string(), ''), + cache_age: v.number(), + html: v.string(), + width: v.fallback(v.nullable(v.number()), null), + height: v.fallback(v.nullable(v.number()), null), + }), + response.json, + ); }, }; @@ -5141,7 +5790,9 @@ class PlApiClient { * @see {@link https://docs.pleroma.social/backend/development/API/chats/#creating-or-getting-a-chat} */ createChat: async (accountId: string) => { - const response = await this.request(`/api/v1/pleroma/chats/by-account-id/${accountId}`, { method: 'POST' }); + const response = await this.request(`/api/v1/pleroma/chats/by-account-id/${accountId}`, { + method: 'POST', + }); return v.parse(chatSchema, response.json); }, @@ -5161,7 +5812,10 @@ class PlApiClient { * @see {@link https://docs.pleroma.social/backend/development/API/chats/#marking-a-chat-as-read} */ markChatAsRead: async (chatId: string, last_read_id: string) => { - const response = await this.request(`/api/v1/pleroma/chats/${chatId}/read`, { method: 'POST', body: { last_read_id } }); + const response = await this.request(`/api/v1/pleroma/chats/${chatId}/read`, { + method: 'POST', + body: { last_read_id }, + }); return v.parse(chatSchema, response.json); }, @@ -5172,7 +5826,10 @@ class PlApiClient { * https://docs.pleroma.social/backend/development/API/chats/#marking-a-single-chat-message-as-read */ markChatMessageAsRead: async (chatId: string, chatMessageId: string) => { - const response = await this.request(`/api/v1/pleroma/chats/${chatId}/messages/${chatMessageId}/read`, { method: 'POST' }); + const response = await this.request( + `/api/v1/pleroma/chats/${chatId}/messages/${chatMessageId}/read`, + { method: 'POST' }, + ); return v.parse(chatSchema, response.json); }, @@ -5182,14 +5839,14 @@ class PlApiClient { * This will return a list of chats that you have been involved in, sorted by their last update (so new chats will be at the top). * @see {@link https://docs.pleroma.social/backend/development/API/chats/#getting-a-list-of-chats} */ - getChats: async (params?: GetChatsParams) => + getChats: (params?: GetChatsParams) => this.#paginatedGet('/api/v2/pleroma/chats', { params }, chatSchema), /** * Getting the messages for a Chat * For a given Chat id, you can get the associated messages with */ - getChatMessages: async (chatId: string, params?: GetChatMessagesParams) => + getChatMessages: (chatId: string, params?: GetChatMessagesParams) => this.#paginatedGet(`/api/v1/pleroma/chats/${chatId}/messages`, { params }, chatMessageSchema), /** @@ -5198,7 +5855,10 @@ class PlApiClient { * @see {@link https://docs.pleroma.social/backend/development/API/chats/#posting-a-chat-message} */ createChatMessage: async (chatId: string, params: CreateChatMessageParams) => { - const response = await this.request(`/api/v1/pleroma/chats/${chatId}/messages`, { method: 'POST', body: params }); + const response = await this.request(`/api/v1/pleroma/chats/${chatId}/messages`, { + method: 'POST', + body: params, + }); return v.parse(chatMessageSchema, response.json); }, @@ -5209,7 +5869,9 @@ class PlApiClient { * @see {@link https://docs.pleroma.social/backend/development/API/chats/#deleting-a-chat-message} */ deleteChatMessage: async (chatId: string, messageId: string) => { - const response = await this.request(`/api/v1/pleroma/chats/${chatId}/messages/${messageId}`, { method: 'DELETE' }); + const response = await this.request(`/api/v1/pleroma/chats/${chatId}/messages/${messageId}`, { + method: 'DELETE', + }); return v.parse(chatMessageSchema, response.json); }, @@ -5227,10 +5889,16 @@ class PlApiClient { }; public readonly shoutbox = { - connect: (token: string, { onMessage, onMessages }: { - onMessages: (messages: Array) => void; - onMessage: (message: ShoutMessage) => void; - }) => { + connect: ( + token: string, + { + onMessage, + onMessages, + }: { + onMessages: (messages: Array) => void; + onMessage: (message: ShoutMessage) => void; + }, + ) => { let counter = 2; let intervalId: NodeJS.Timeout; if (this.#shoutSocket) return this.#shoutSocket; @@ -5265,7 +5933,7 @@ class PlApiClient { this.#shoutSocket = { message: (text: string) => { // guess this is meant to be incremented on each call but idk - ws.send(JSON.stringify(['3', `${++counter}`, 'chat:public', 'new_msg', { 'text': text }])); + ws.send(JSON.stringify(['3', `${++counter}`, 'chat:public', 'new_msg', { text: text }])); }, close: () => { ws.close(); @@ -5284,7 +5952,10 @@ class PlApiClient { * @see {@link https://codeberg.org/mkljczk/nicolex/src/branch/develop/docs/development/API/pleroma_api.md#api-v1-pleroma-events} */ createEvent: async (params: CreateEventParams) => { - const response = await this.request('/api/v1/pleroma/events', { method: 'POST', body: params }); + const response = await this.request('/api/v1/pleroma/events', { + method: 'POST', + body: params, + }); return v.parse(statusSchema, response.json); }, @@ -5294,7 +5965,10 @@ class PlApiClient { * @see {@link https://codeberg.org/mkljczk/nicolex/src/branch/develop/docs/development/API/pleroma_api.md#api-v1-pleroma-events-id} */ editEvent: async (statusId: string, params: EditEventParams) => { - const response = await this.request(`/api/v1/pleroma/events/${statusId}`, { method: 'PUT', body: params }); + const response = await this.request(`/api/v1/pleroma/events/${statusId}`, { + method: 'PUT', + body: params, + }); return v.parse(statusSchema, response.json); }, @@ -5303,32 +5977,50 @@ class PlApiClient { * Gets user's joined events * @see {@link https://codeberg.org/mkljczk/nicolex/src/branch/develop/docs/development/API/pleroma_api.md#api-v1-pleroma-events-joined_events} */ - getJoinedEvents: async (state?: 'pending' | 'reject' | 'accept', params?: GetJoinedEventsParams) => - this.#paginatedGet('/api/v1/pleroma/events/joined_events', { params: { ...params, state } }, statusSchema), + getJoinedEvents: (state?: 'pending' | 'reject' | 'accept', params?: GetJoinedEventsParams) => + this.#paginatedGet( + '/api/v1/pleroma/events/joined_events', + { params: { ...params, state } }, + statusSchema, + ), /** * Gets event participants * @see {@link https://codeberg.org/mkljczk/nicolex/src/branch/develop/docs/development/API/pleroma_api.md#api-v1-pleroma-events-id-participations} */ - getEventParticipations: async (statusId: string, params?: GetEventParticipationsParams) => - this.#paginatedGet(`/api/v1/pleroma/events/${statusId}/participations`, { params }, accountSchema), + getEventParticipations: (statusId: string, params?: GetEventParticipationsParams) => + this.#paginatedGet( + `/api/v1/pleroma/events/${statusId}/participations`, + { params }, + accountSchema, + ), /** * Gets event participation requests * @see {@link https://codeberg.org/mkljczk/nicolex/src/branch/develop/docs/development/API/pleroma_api.md#api-v1-pleroma-events-id-participation_requests} */ - getEventParticipationRequests: async (statusId: string, params?: GetEventParticipationRequestsParams) => - this.#paginatedGet(`/api/v1/pleroma/events/${statusId}/participation_requests`, { params }, v.object({ - account: accountSchema, - participation_message: v.fallback(v.string(), ''), - })), + getEventParticipationRequests: ( + statusId: string, + params?: GetEventParticipationRequestsParams, + ) => + this.#paginatedGet( + `/api/v1/pleroma/events/${statusId}/participation_requests`, + { params }, + v.object({ + account: accountSchema, + participation_message: v.fallback(v.string(), ''), + }), + ), /** * Accepts user to the event * @see {@link https://codeberg.org/mkljczk/nicolex/src/branch/develop/docs/development/API/pleroma_api.md#api-v1-pleroma-events-id-participation_requests-participant_id-authorize} */ acceptEventParticipationRequest: async (statusId: string, accountId: string) => { - const response = await this.request(`/api/v1/pleroma/events/${statusId}/participation_requests/${accountId}/authorize`, { method: 'POST' }); + const response = await this.request( + `/api/v1/pleroma/events/${statusId}/participation_requests/${accountId}/authorize`, + { method: 'POST' }, + ); return v.parse(statusSchema, response.json); }, @@ -5338,7 +6030,10 @@ class PlApiClient { * @see {@link https://codeberg.org/mkljczk/nicolex/src/branch/develop/docs/development/API/pleroma_api.md#api-v1-pleroma-events-id-participation_requests-participant_id-reject} */ rejectEventParticipationRequest: async (statusId: string, accountId: string) => { - const response = await this.request(`/api/v1/pleroma/events/${statusId}/participation_requests/${accountId}/reject`, { method: 'POST' }); + const response = await this.request( + `/api/v1/pleroma/events/${statusId}/participation_requests/${accountId}/reject`, + { method: 'POST' }, + ); return v.parse(statusSchema, response.json); }, @@ -5348,7 +6043,10 @@ class PlApiClient { * @see {@link https://codeberg.org/mkljczk/nicolex/src/branch/develop/docs/development/API/pleroma_api.md#api-v1-pleroma-events-id-join} */ joinEvent: async (statusId: string, participation_message?: string) => { - const response = await this.request(`/api/v1/pleroma/events/${statusId}/join`, { method: 'POST', body: { participation_message } }); + const response = await this.request(`/api/v1/pleroma/events/${statusId}/join`, { + method: 'POST', + body: { participation_message }, + }); return v.parse(statusSchema, response.json); }, @@ -5358,7 +6056,9 @@ class PlApiClient { * @see {@link https://codeberg.org/mkljczk/nicolex/src/branch/develop/docs/development/API/pleroma_api.md#api-v1-pleroma-events-id-leave} */ leaveEvent: async (statusId: string) => { - const response = await this.request(`/api/v1/pleroma/events/${statusId}/leave`, { method: 'POST' }); + const response = await this.request(`/api/v1/pleroma/events/${statusId}/leave`, { + method: 'POST', + }); return v.parse(statusSchema, response.json); }, @@ -5368,7 +6068,9 @@ class PlApiClient { * @see {@link https://codeberg.org/mkljczk/nicolex/src/branch/develop/docs/development/API/pleroma_api.md#event-ics-file} */ getEventIcs: async (statusId: string) => { - const response = await this.request(`/api/v1/pleroma/events/${statusId}/ics`, { contentType: '' }); + const response = await this.request(`/api/v1/pleroma/events/${statusId}/ics`, { + contentType: '', + }); return response.data; }, @@ -5380,7 +6082,7 @@ class PlApiClient { * * Requires features{@link Features.interactionRequests}. */ - getInteractionRequests: async (params?: GetInteractionRequestsParams) => + getInteractionRequests: (params?: GetInteractionRequestsParams) => this.#paginatedGet('/api/v1/interaction_requests', { params }, interactionRequestSchema), /** @@ -5400,7 +6102,10 @@ class PlApiClient { * Requires features{@link Features.interactionRequests}. */ authorizeInteractionRequest: async (interactionRequestId: string) => { - const response = await this.request(`/api/v1/interaction_requests/${interactionRequestId}/authorize`, { method: 'POST' }); + const response = await this.request( + `/api/v1/interaction_requests/${interactionRequestId}/authorize`, + { method: 'POST' }, + ); return v.parse(interactionRequestSchema, response.json); }, @@ -5411,7 +6116,10 @@ class PlApiClient { * Requires features{@link Features.interactionRequests}. */ rejectInteractionRequest: async (interactionRequestId: string) => { - const response = await this.request(`/api/v1/interaction_requests/${interactionRequestId}/authorize`, { method: 'POST' }); + const response = await this.request( + `/api/v1/interaction_requests/${interactionRequestId}/authorize`, + { method: 'POST' }, + ); return v.parse(interactionRequestSchema, response.json); }, @@ -5449,7 +6157,10 @@ class PlApiClient { * Requires features{@link Features.antennas}. */ updateAntenna: async (antennaId: string, params: UpdateAntennaParams) => { - const response = await this.request(`/api/v1/antennas/${antennaId}`, { method: 'PUT', body: params }); + const response = await this.request(`/api/v1/antennas/${antennaId}`, { + method: 'PUT', + body: params, + }); return v.parse(antennaSchema, response.json); }, @@ -5458,7 +6169,9 @@ class PlApiClient { * Requires features{@link Features.antennas}. */ deleteAntenna: async (antennaId: string) => { - const response = await this.request<{}>(`/api/v1/antennas/${antennaId}`, { method: 'DELETE' }); + const response = await this.request(`/api/v1/antennas/${antennaId}`, { + method: 'DELETE', + }); return response.json; }, @@ -5466,14 +6179,14 @@ class PlApiClient { /** * Requires features{@link Features.antennas}. */ - getAntennaAccounts: async (antennaId: string) => + getAntennaAccounts: (antennaId: string) => this.#paginatedGet(`/api/v1/antennas/${antennaId}/accounts`, {}, accountSchema), /** * Requires features{@link Features.antennas}. */ addAntennaAccounts: async (antennaId: string, accountIds: Array) => { - const response = await this.request<{}>(`/api/v1/antennas/${antennaId}/accounts`, { + const response = await this.request(`/api/v1/antennas/${antennaId}/accounts`, { method: 'POST', body: { account_ids: accountIds }, }); @@ -5485,7 +6198,7 @@ class PlApiClient { * Requires features{@link Features.antennas}. */ removeAntennaAccounts: async (antennaId: string, accountIds: Array) => { - const response = await this.request<{}>(`/api/v1/antennas/${antennaId}/accounts`, { + const response = await this.request(`/api/v1/antennas/${antennaId}/accounts`, { method: 'DELETE', body: { account_ids: accountIds }, }); @@ -5496,17 +6209,20 @@ class PlApiClient { /** * Requires features{@link Features.antennas}. */ - getAntennaExcludedAccounts: async (antennaId: string) => + getAntennaExcludedAccounts: (antennaId: string) => this.#paginatedGet(`/api/v1/antennas/${antennaId}/exclude_accounts`, {}, accountSchema), /** * Requires features{@link Features.antennas}. */ addAntennaExcludedAccounts: async (antennaId: string, accountIds: Array) => { - const response = await this.request<{}>(`/api/v1/antennas/${antennaId}/exclude_accounts`, { - method: 'POST', - body: { account_ids: accountIds }, - }); + const response = await this.request( + `/api/v1/antennas/${antennaId}/exclude_accounts`, + { + method: 'POST', + body: { account_ids: accountIds }, + }, + ); return response.json; }, @@ -5515,10 +6231,13 @@ class PlApiClient { * Requires features{@link Features.antennas}. */ removeAntennaExcludedAccounts: async (antennaId: string, accountIds: Array) => { - const response = await this.request<{}>(`/api/v1/antennas/${antennaId}/exclude_accounts`, { - method: 'DELETE', - body: { account_ids: accountIds }, - }); + const response = await this.request( + `/api/v1/antennas/${antennaId}/exclude_accounts`, + { + method: 'DELETE', + body: { account_ids: accountIds }, + }, + ); return response.json; }, @@ -5529,17 +6248,20 @@ class PlApiClient { getAntennaDomains: async (antennaId: string) => { const response = await this.request(`/api/v1/antennas/${antennaId}/domains`); - return v.parse(v.object({ - domains: filteredArray(v.string()), - exclude_domains: filteredArray(v.string()), - }), response.json); + return v.parse( + v.object({ + domains: filteredArray(v.string()), + exclude_domains: filteredArray(v.string()), + }), + response.json, + ); }, /** * Requires features{@link Features.antennas}. */ addAntennaDomains: async (antennaId: string, domains: Array) => { - const response = await this.request<{}>(`/api/v1/antennas/${antennaId}/domains`, { + const response = await this.request(`/api/v1/antennas/${antennaId}/domains`, { method: 'POST', body: { domains }, }); @@ -5551,7 +6273,7 @@ class PlApiClient { * Requires features{@link Features.antennas}. */ removeAntennaDomains: async (antennaId: string, domains: Array) => { - const response = await this.request<{}>(`/api/v1/antennas/${antennaId}/domains`, { + const response = await this.request(`/api/v1/antennas/${antennaId}/domains`, { method: 'DELETE', body: { domains }, }); @@ -5563,10 +6285,13 @@ class PlApiClient { * Requires features{@link Features.antennas}. */ addAntennaExcludedDomains: async (antennaId: string, domains: Array) => { - const response = await this.request<{}>(`/api/v1/antennas/${antennaId}/exclude_domains`, { - method: 'POST', - body: { domains }, - }); + const response = await this.request( + `/api/v1/antennas/${antennaId}/exclude_domains`, + { + method: 'POST', + body: { domains }, + }, + ); return response.json; }, @@ -5575,10 +6300,13 @@ class PlApiClient { * Requires features{@link Features.antennas}. */ removeAntennaExcludedDomains: async (antennaId: string, domains: Array) => { - const response = await this.request<{}>(`/api/v1/antennas/${antennaId}/exclude_domains`, { - method: 'DELETE', - body: { domains }, - }); + const response = await this.request( + `/api/v1/antennas/${antennaId}/exclude_domains`, + { + method: 'DELETE', + body: { domains }, + }, + ); return response.json; }, @@ -5589,17 +6317,20 @@ class PlApiClient { getAntennaKeywords: async (antennaId: string) => { const response = await this.request(`/api/v1/antennas/${antennaId}/keywords`); - return v.parse(v.object({ - keywords: filteredArray(v.string()), - exclude_keywords: filteredArray(v.string()), - }), response.json); + return v.parse( + v.object({ + keywords: filteredArray(v.string()), + exclude_keywords: filteredArray(v.string()), + }), + response.json, + ); }, /** * Requires features{@link Features.antennas}. */ addAntennaKeywords: async (antennaId: string, keywords: Array) => { - const response = await this.request<{}>(`/api/v1/antennas/${antennaId}/keywords`, { + const response = await this.request(`/api/v1/antennas/${antennaId}/keywords`, { method: 'POST', body: { keywords }, }); @@ -5611,7 +6342,7 @@ class PlApiClient { * Requires features{@link Features.antennas}. */ removeAntennaKeywords: async (antennaId: string, keywords: Array) => { - const response = await this.request<{}>(`/api/v1/antennas/${antennaId}/keywords`, { + const response = await this.request(`/api/v1/antennas/${antennaId}/keywords`, { method: 'DELETE', body: { keywords }, }); @@ -5623,10 +6354,13 @@ class PlApiClient { * Requires features{@link Features.antennas}. */ addAntennaExcludedKeywords: async (antennaId: string, keywords: Array) => { - const response = await this.request<{}>(`/api/v1/antennas/${antennaId}/exclude_keywords`, { - method: 'POST', - body: { keywords }, - }); + const response = await this.request( + `/api/v1/antennas/${antennaId}/exclude_keywords`, + { + method: 'POST', + body: { keywords }, + }, + ); return response.json; }, @@ -5635,10 +6369,13 @@ class PlApiClient { * Requires features{@link Features.antennas}. */ removeAntennaExcludedKeywords: async (antennaId: string, keywords: Array) => { - const response = await this.request<{}>(`/api/v1/antennas/${antennaId}/exclude_keywords`, { - method: 'DELETE', - body: { keywords }, - }); + const response = await this.request( + `/api/v1/antennas/${antennaId}/exclude_keywords`, + { + method: 'DELETE', + body: { keywords }, + }, + ); return response.json; }, @@ -5649,19 +6386,22 @@ class PlApiClient { getAntennaTags: async (antennaId: string) => { const response = await this.request(`/api/v1/antennas/${antennaId}/tags`); - return v.parse(v.object({ - tags: filteredArray(v.string()), - exclude_tags: filteredArray(v.string()), - }), response.json); + return v.parse( + v.object({ + tags: filteredArray(v.string()), + exclude_tags: filteredArray(v.string()), + }), + response.json, + ); }, /** * Requires features{@link Features.antennas}. */ - addAntennaTag: async (antennaId: string, tag: string) => { - const response = await this.request<{}>(`/api/v1/antennas/${antennaId}/tags`, { + addAntennaTags: async (antennaId: string, tags: Array) => { + const response = await this.request(`/api/v1/antennas/${antennaId}/tags`, { method: 'POST', - body: { tags: [tag] }, + body: { tags }, }); return response.json; @@ -5670,10 +6410,10 @@ class PlApiClient { /** * Requires features{@link Features.antennas}. */ - removeAntennaTag: async (antennaId: string, tag: string) => { - const response = await this.request<{}>(`/api/v1/antennas/${antennaId}/tags`, { + removeAntennaTags: async (antennaId: string, tags: Array) => { + const response = await this.request(`/api/v1/antennas/${antennaId}/tags`, { method: 'DELETE', - body: { tags: [tag] }, + body: { tags }, }); return response.json; @@ -5683,10 +6423,13 @@ class PlApiClient { * Requires features{@link Features.antennas}. */ addAntennaExcludedTags: async (antennaId: string, tags: Array) => { - const response = await this.request<{}>(`/api/v1/antennas/${antennaId}/exclude_tags`, { - method: 'POST', - body: { tags }, - }); + const response = await this.request( + `/api/v1/antennas/${antennaId}/exclude_tags`, + { + method: 'POST', + body: { tags }, + }, + ); return response.json; }, @@ -5695,10 +6438,13 @@ class PlApiClient { * Requires features{@link Features.antennas}. */ removeAntennaExcludedTags: async (antennaId: string, tags: Array) => { - const response = await this.request<{}>(`/api/v1/antennas/${antennaId}/exclude_tags`, { - method: 'DELETE', - body: { tags }, - }); + const response = await this.request( + `/api/v1/antennas/${antennaId}/exclude_tags`, + { + method: 'DELETE', + body: { tags }, + }, + ); return response.json; }, @@ -5736,7 +6482,10 @@ class PlApiClient { * Requires features{@link Features.circles}. */ updateCircle: async (circleId: string, title: string) => { - const response = await this.request(`/api/v1/circles/${circleId}`, { method: 'PUT', body: { title } }); + const response = await this.request(`/api/v1/circles/${circleId}`, { + method: 'PUT', + body: { title }, + }); return v.parse(circleSchema, response.json); }, @@ -5745,7 +6494,9 @@ class PlApiClient { * Requires features{@link Features.circles}. */ deleteCircle: async (circleId: string) => { - const response = await this.request<{}>(`/api/v1/circles/${circleId}`, { method: 'DELETE' }); + const response = await this.request(`/api/v1/circles/${circleId}`, { + method: 'DELETE', + }); return response.json; }, @@ -5754,7 +6505,7 @@ class PlApiClient { * View accounts in a circle * Requires features{@link Features.circles}. */ - getCircleAccounts: async (circleId: string, params?: GetCircleAccountsParams) => + getCircleAccounts: (circleId: string, params?: GetCircleAccountsParams) => this.#paginatedGet(`/api/v1/circles/${circleId}/accounts`, { params }, accountSchema), /** @@ -5763,11 +6514,12 @@ class PlApiClient { * Requires features{@link Features.circles}. */ addCircleAccounts: async (circleId: string, accountIds: string[]) => { - const response = await this.request(`/api/v1/circles/${circleId}/accounts`, { - method: 'POST', body: { account_ids: accountIds }, + const response = await this.request(`/api/v1/circles/${circleId}/accounts`, { + method: 'POST', + body: { account_ids: accountIds }, }); - return response.json as {}; + return response.json; }, /** @@ -5776,11 +6528,12 @@ class PlApiClient { * Requires features{@link Features.circles}. */ deleteCircleAccounts: async (circleId: string, accountIds: string[]) => { - const response = await this.request(`/api/v1/circles/${circleId}/accounts`, { - method: 'DELETE', body: { account_ids: accountIds }, + const response = await this.request(`/api/v1/circles/${circleId}/accounts`, { + method: 'DELETE', + body: { account_ids: accountIds }, }); - return response.json as {}; + return response.json; }, getCircleStatuses: (circleId: string, params: GetCircleStatusesParams) => @@ -5801,7 +6554,10 @@ class PlApiClient { * Requires features{@link Features.rssFeedSubscriptions}. */ createRssFeedSubscription: async (url: string) => { - const response = await this.request('/api/v1/pleroma/rss_feed_subscriptions', { method: 'POST', body: { url } }); + const response = await this.request('/api/v1/pleroma/rss_feed_subscriptions', { + method: 'POST', + body: { url }, + }); return v.parse(rssFeedSchema, response.json); }, @@ -5810,7 +6566,10 @@ class PlApiClient { * Requires features{@link Features.rssFeedSubscriptions}. */ deleteRssFeedSubscription: async (url: string) => { - const response = await this.request<{}>('/api/v1/pleroma/rss_feed_subscriptions', { method: 'DELETE', body: { url } }); + const response = await this.request('/api/v1/pleroma/rss_feed_subscriptions', { + method: 'DELETE', + body: { url }, + }); return response.json; }, @@ -5824,8 +6583,11 @@ class PlApiClient { * @param subscriberId - The subscriber ID. * @param duration - The subscription duration (in seconds). */ - createSubscription: async(subscriberId: string, duration: number) => { - const response = await this.request('/api/v1/subscriptions', { method: 'POST', body: { subscriber_id: subscriberId, duration } }); + createSubscription: async (subscriberId: string, duration: number) => { + const response = await this.request('/api/v1/subscriptions', { + method: 'POST', + body: { subscriber_id: subscriberId, duration }, + }); return v.parse(subscriptionDetailsSchema, response.json); }, @@ -5850,8 +6612,16 @@ class PlApiClient { * @param price - Subscription price (only for Monero) * @param payoutAddress - Payout address (only for Monero) */ - updateSubscription: async(type: 'monero', chainId?: string, price?: number, payoutAddress?: string) => { - const response = await this.request('/api/v1/subscriptions/options', { method: 'POST', body: { type, chain_id: chainId, price, payout_address: payoutAddress } }); + updateSubscription: async ( + type: 'monero', + chainId?: string, + price?: number, + payoutAddress?: string, + ) => { + const response = await this.request('/api/v1/subscriptions/options', { + method: 'POST', + body: { type, chain_id: chainId, price, payout_address: payoutAddress }, + }); return v.parse(accountSchema, response.json); }, @@ -5863,8 +6633,10 @@ class PlApiClient { * @param senderId - Sender ID. * @param recipientId - Recipient ID. */ - findSubscription: async(senderId: string, recipientId: string) => { - const response = await this.request('/api/v1/subscriptions/find', { params: { sender_id: senderId, recipient_id: recipientId } }); + findSubscription: async (senderId: string, recipientId: string) => { + const response = await this.request('/api/v1/subscriptions/find', { + params: { sender_id: senderId, recipient_id: recipientId }, + }); return v.parse(subscriptionDetailsSchema, response.json); }, @@ -5878,11 +6650,19 @@ class PlApiClient { * @param chainId - CAIP-2 chain ID. * @param amount - Requested payment amount (in atomic units). */ - createInvoice: async(senderId: string, recipientId: string, chainId: string, amount: number) => { + createInvoice: async ( + senderId: string, + recipientId: string, + chainId: string, + amount: number, + ) => { const response = await this.request('/api/v1/subscriptions/invoices', { method: 'POST', body: { - sender_id: senderId, recipient_id: recipientId, chain_id: chainId, amount, + sender_id: senderId, + recipient_id: recipientId, + chain_id: chainId, + amount, }, }); @@ -5895,7 +6675,7 @@ class PlApiClient { * Requires features{@link Features.invoices}. * @param invoiceId - Invoice ID */ - getInvoice: async(invoiceId: string) => { + getInvoice: async (invoiceId: string) => { const response = await this.request(`/api/v1/subscriptions/invoices/${invoiceId}`); return v.parse(subscriptionInvoiceSchema, response.json); @@ -5907,7 +6687,7 @@ class PlApiClient { * Requires features{@link Features.invoices}. * @param invoiceId - Invoice ID */ - cancelInvoice: async(invoiceId: string) => { + cancelInvoice: async (invoiceId: string) => { const response = await this.request(`/api/v1/subscriptions/invoices/${invoiceId}`, { method: 'DELETE', }); @@ -5958,7 +6738,7 @@ class PlApiClient { deleteFolder: async (id: string) => { await this.#getIceshrimpAccessToken(); - const response = await this.request<{}>(`/api/iceshrimp/drive/folder/${id}`, { + const response = await this.request(`/api/iceshrimp/drive/folder/${id}`, { method: 'DELETE', }); @@ -6011,7 +6791,7 @@ class PlApiClient { deleteFile: async (id: string) => { await this.#getIceshrimpAccessToken(); - const response = await this.request<{}>(`/api/iceshrimp/drive/${id}`, { + const response = await this.request>(`/api/iceshrimp/drive/${id}`, { method: 'DELETE', }); @@ -6074,7 +6854,7 @@ class PlApiClient { }, markStoryAsViewed: async (storyId: string) => { - const response = await this.request<{}>('/api/web/stories/v1/viewed', { + const response = await this.request('/api/web/stories/v1/viewed', { method: 'POST', body: { id: storyId }, }); @@ -6083,7 +6863,7 @@ class PlApiClient { }, createStoryReaction: async (storyId: string, emoji: string) => { - const response = await this.request<{}>('/api/web/stories/v1/react', { + const response = await this.request('/api/web/stories/v1/react', { method: 'POST', body: { sid: storyId, reaction: emoji }, }); @@ -6092,7 +6872,7 @@ class PlApiClient { }, createStoryComment: async (storyId: string, comment: string) => { - const response = await this.request<{}>('/api/web/stories/v1/comment', { + const response = await this.request('/api/web/stories/v1/comment', { method: 'POST', body: { sid: storyId, caption: comment }, }); @@ -6101,7 +6881,7 @@ class PlApiClient { }, createStoryPoll: async (params: CreateStoryPollParams) => { - const response = await this.request<{}>('/api/web/stories/v1/publish/poll', { + const response = await this.request('/api/web/stories/v1/publish/poll', { method: 'POST', body: params, }); @@ -6110,7 +6890,7 @@ class PlApiClient { }, storyPollVote: async (storyId: string, choiceId: number) => { - const response = await this.request<{}>('/api/web/stories/v1/publish/poll', { + const response = await this.request('/api/web/stories/v1/publish/poll', { method: 'POST', body: { sid: storyId, ci: choiceId }, }); @@ -6119,7 +6899,7 @@ class PlApiClient { }, reportStory: async (storyId: string, type: StoryReportType) => { - const response = await this.request<{}>('/api/web/stories/v1/report', { + const response = await this.request('/api/web/stories/v1/report', { method: 'POST', body: { id: storyId, type }, }); @@ -6138,7 +6918,7 @@ class PlApiClient { }, cropPhoto: async (mediaId: string, params: CropStoryPhotoParams) => { - const response = await this.request<{}>('/api/web/stories/v1/crop', { + const response = await this.request('/api/web/stories/v1/crop', { method: 'POST', body: { media_id: mediaId, ...params }, }); @@ -6147,7 +6927,7 @@ class PlApiClient { }, createStory: async (mediaId: string, params: CreateStoryParams) => { - const response = await this.request<{}>('/api/web/stories/v1/publish', { + const response = await this.request('/api/web/stories/v1/publish', { method: 'POST', body: { media_id: mediaId, ...params }, }); @@ -6156,7 +6936,7 @@ class PlApiClient { }, deleteStory: async (storyId: string) => { - const response = await this.request<{}>(`/api/web/stories/v1/delete/${storyId}`, { + const response = await this.request(`/api/web/stories/v1/delete/${storyId}`, { method: 'DELETE', }); @@ -6167,7 +6947,7 @@ class PlApiClient { /** Routes that are not part of any stable release */ public readonly experimental = { admin: { - /** @see {@link https://github.com/mastodon/mastodon/pull/19059} */ + /** @see {@link https://github.com/mastodon/mastodon/pull/19059} */ groups: { /** list groups known to the instance. Mimics the interface of `/api/v1/admin/accounts` */ getGroups: async (params?: AdminGetGroupsParams) => { @@ -6185,21 +6965,27 @@ class PlApiClient { /** suspends a group */ suspendGroup: async (groupId: string) => { - const response = await this.request(`/api/v1/admin/groups/${groupId}/suspend`, { method: 'POST' }); + const response = await this.request(`/api/v1/admin/groups/${groupId}/suspend`, { + method: 'POST', + }); return v.parse(groupSchema, response.json); }, /** lift a suspension */ unsuspendGroup: async (groupId: string) => { - const response = await this.request(`/api/v1/admin/groups/${groupId}/unsuspend`, { method: 'POST' }); + const response = await this.request(`/api/v1/admin/groups/${groupId}/unsuspend`, { + method: 'POST', + }); return v.parse(groupSchema, response.json); }, /** deletes an already-suspended group */ deleteGroup: async (groupId: string) => { - const response = await this.request(`/api/v1/admin/groups/${groupId}`, { method: 'DELETE' }); + const response = await this.request(`/api/v1/admin/groups/${groupId}`, { + method: 'DELETE', + }); return v.parse(groupSchema, response.json); }, @@ -6227,7 +7013,12 @@ class PlApiClient { if (this.features.version.software === PIXELFED) { response = await this.request('/api/v0/groups/create', { method: 'POST', - body: { ...params, name: params.display_name, description: params.note, membership: 'public' }, + body: { + ...params, + name: params.display_name, + description: params.note, + membership: 'public', + }, contentType: params.avatar || params.header ? '' : undefined, }); @@ -6274,67 +7065,94 @@ class PlApiClient { let response; if (this.features.version.software === PIXELFED) { - response = await this.request('/api/v0/groups/delete', { method: 'POST', params: { gid: groupId } }); + response = await this.request('/api/v0/groups/delete', { + method: 'POST', + params: { gid: groupId }, + }); } else { - response = await this.request(`/api/v1/groups/${groupId}`, { method: 'DELETE' }); + response = await this.request(`/api/v1/groups/${groupId}`, { + method: 'DELETE', + }); } - return response.json as {}; + return response.json; }, /** Has an optional role attribute that can be used to filter by role (valid roles are `"admin"`, `"moderator"`, `"user"`). */ - getGroupMemberships: async (groupId: string, role?: GroupRole, params?: GetGroupMembershipsParams) => + getGroupMemberships: ( + groupId: string, + role?: GroupRole, + params?: GetGroupMembershipsParams, + ) => this.#paginatedGet( - this.features.version.software === PIXELFED ? `/api/v0/groups/members/list?gid=${groupId}` : `/api/v1/groups/${groupId}/memberships`, + this.features.version.software === PIXELFED + ? `/api/v0/groups/members/list?gid=${groupId}` + : `/api/v1/groups/${groupId}/memberships`, { params: { ...params, role } }, groupMemberSchema, ), /** returns an array of `Account` entities representing pending requests to join a group */ - getGroupMembershipRequests: async (groupId: string, params?: GetGroupMembershipRequestsParams) => + getGroupMembershipRequests: (groupId: string, params?: GetGroupMembershipRequestsParams) => this.#paginatedGet( - this.features.version.software === PIXELFED ? `/api/v0/groups/members/requests?gid=${groupId}` : `/api/v1/groups/${groupId}/membership_requests`, + this.features.version.software === PIXELFED + ? `/api/v0/groups/members/requests?gid=${groupId}` + : `/api/v1/groups/${groupId}/membership_requests`, { params }, accountSchema, ), /** accept a pending request to become a group member */ acceptGroupMembershipRequest: async (groupId: string, accountId: string) => { - const response = await this.request(`/api/v1/groups/${groupId}/membership_requests/${accountId}/authorize`, { method: 'POST' }); + const response = await this.request( + `/api/v1/groups/${groupId}/membership_requests/${accountId}/authorize`, + { method: 'POST' }, + ); - return response.json as {}; + return response.json; }, /** reject a pending request to become a group member */ rejectGroupMembershipRequest: async (groupId: string, accountId: string) => { - const response = await this.request(`/api/v1/groups/${groupId}/membership_requests/${accountId}/reject`, { method: 'POST' }); + const response = await this.request( + `/api/v1/groups/${groupId}/membership_requests/${accountId}/reject`, + { method: 'POST' }, + ); - return response.json as {}; + return response.json; }, /** delete a group post (actually marks it as `revoked` if it is a local post) */ deleteGroupStatus: async (groupId: string, statusId: string) => { - const response = await this.request(`/api/v1/groups/${groupId}/statuses/${statusId}`, { method: 'DELETE' }); + const response = await this.request(`/api/v1/groups/${groupId}/statuses/${statusId}`, { + method: 'DELETE', + }); return v.parse(statusSchema, response.json); }, /** list accounts blocked from interacting with the group */ - getGroupBlocks: async (groupId: string, params?: GetGroupBlocksParams) => + getGroupBlocks: (groupId: string, params?: GetGroupBlocksParams) => this.#paginatedGet(`/api/v1/groups/${groupId}/blocks`, { params }, accountSchema), /** block one or more users. If they were in the group, they are also kicked of it */ blockGroupUsers: async (groupId: string, accountIds: string[]) => { - const response = await this.request(`/api/v1/groups/${groupId}/blocks`, { method: 'POST', params: { account_ids: accountIds } }); + const response = await this.request(`/api/v1/groups/${groupId}/blocks`, { + method: 'POST', + params: { account_ids: accountIds }, + }); - return response.json as {}; + return response.json; }, /** block one or more users. If they were in the group, they are also kicked of it */ unblockGroupUsers: async (groupId: string, accountIds: string[]) => { - const response = await this.request(`/api/v1/groups/${groupId}/blocks`, { method: 'DELETE', params: { account_ids: accountIds } }); + const response = await this.request(`/api/v1/groups/${groupId}/blocks`, { + method: 'DELETE', + params: { account_ids: accountIds }, + }); - return response.json as {}; + return response.json; }, /** joins (or request to join) a given group */ @@ -6353,27 +7171,38 @@ class PlApiClient { /** kick one or more group members */ kickGroupUsers: async (groupId: string, accountIds: string[]) => { - const response = await this.request(`/api/v1/groups/${groupId}/kick`, { method: 'POST', params: { account_ids: accountIds } }); + const response = await this.request(`/api/v1/groups/${groupId}/kick`, { + method: 'POST', + params: { account_ids: accountIds }, + }); - return response.json as {}; + return response.json; }, /** promote one or more accounts to role `new_role`. An error is returned if any of those accounts has a higher role than `new_role` already, or if the role is higher than the issuing user's. Valid roles are `admin`, and `moderator` and `user`. */ promoteGroupUsers: async (groupId: string, accountIds: string[], role: GroupRole) => { - const response = await this.request(`/api/v1/groups/${groupId}/promote`, { method: 'POST', params: { account_ids: accountIds, role } }); + const response = await this.request(`/api/v1/groups/${groupId}/promote`, { + method: 'POST', + params: { account_ids: accountIds, role }, + }); return v.parse(filteredArray(groupMemberSchema), response.json); }, /** demote one or more accounts to role `new_role`. Returns an error unless every of the target account has a strictly lower role than the user (you cannot demote someone with the same role as you), or if any target account already has a role lower than `new_role`. Valid roles are `admin`, `moderator` and `user`. */ demoteGroupUsers: async (groupId: string, accountIds: string[], role: GroupRole) => { - const response = await this.request(`/api/v1/groups/${groupId}/demote`, { method: 'POST', params: { account_ids: accountIds, role } }); + const response = await this.request(`/api/v1/groups/${groupId}/demote`, { + method: 'POST', + params: { account_ids: accountIds, role }, + }); return v.parse(filteredArray(groupMemberSchema), response.json); }, getGroupRelationships: async (groupIds: string[]) => { - const response = await this.request('/api/v1/groups/relationships', { params: { id: groupIds } }); + const response = await this.request('/api/v1/groups/relationships', { + params: { id: groupIds }, + }); return v.parse(filteredArray(groupRelationshipSchema), response.json); }, @@ -6400,7 +7229,7 @@ class PlApiClient { return this.#accessToken; } - set accessToken(accessToken: string | undefined) { + set accessToken(accessToken: string | undefined) { if (this.#accessToken === accessToken) return; this.#socket?.close(); @@ -6417,17 +7246,13 @@ class PlApiClient { return this.#customAuthorizationToken; } - set customAuthorizationToken(token: string | undefined) { + set customAuthorizationToken(token: string | undefined) { this.#customAuthorizationToken = token; } get instanceInformation() { return this.#instance; } - } -export { - PlApiClient, - PlApiClient as default, -}; +export { PlApiClient as default }; diff --git a/packages/pl-api/lib/directory-client.ts b/packages/pl-api/lib/directory-client.ts index 86cc29c0e..3dfe59cd4 100644 --- a/packages/pl-api/lib/directory-client.ts +++ b/packages/pl-api/lib/directory-client.ts @@ -25,7 +25,6 @@ interface Params { * @category Clients */ class PlApiDirectoryClient { - /** Unused. */ accessToken: string | undefined = undefined; /** Unused. */ @@ -68,10 +67,6 @@ class PlApiDirectoryClient { return v.parse(filteredArray(directoryServerSchema), response.json); } - } -export { - PlApiDirectoryClient, - PlApiDirectoryClient as default, -}; +export { PlApiDirectoryClient, PlApiDirectoryClient as default }; diff --git a/packages/pl-api/lib/entities/account-warning.ts b/packages/pl-api/lib/entities/account-warning.ts index 9ec3eea5b..3abd2c328 100644 --- a/packages/pl-api/lib/entities/account-warning.ts +++ b/packages/pl-api/lib/entities/account-warning.ts @@ -12,10 +12,18 @@ const appealSchema = v.object({ /** * @category Schemas * @see {@link https://docs.joinmastodon.org/entities/AccountWarning/} -*/ + */ const accountWarningSchema = v.object({ id: v.string(), - action: v.picklist(['none', 'disable', 'mark_statuses_as_sensitive', 'delete_statuses', 'sensitive', 'silence', 'suspend']), + action: v.picklist([ + 'none', + 'disable', + 'mark_statuses_as_sensitive', + 'delete_statuses', + 'sensitive', + 'silence', + 'suspend', + ]), text: v.fallback(v.string(), ''), status_ids: v.fallback(v.array(v.string()), []), target_account: accountSchema, diff --git a/packages/pl-api/lib/entities/account.ts b/packages/pl-api/lib/entities/account.ts index a8b220d3e..912718183 100644 --- a/packages/pl-api/lib/entities/account.ts +++ b/packages/pl-api/lib/entities/account.ts @@ -10,9 +10,15 @@ import { roleSchema } from './role'; import { coerceObject, datetimeSchema, filteredArray } from './utils'; const filterBadges = (tags?: string[]) => - tags?.filter(tag => tag.startsWith('badge:')).map(tag => v.parse(roleSchema, { id: tag, name: tag.replace(/^badge:/, '') })); + tags + ?.filter((tag) => tag.startsWith('badge:')) + .map((tag) => v.parse(roleSchema, { id: tag, name: tag.replace(/^badge:/, '') })); -const MKLJCZK_ACCOUNTS = ['https://pl.fediverse.pl/users/mkljczk', 'https://gts.mkljczk.pl/users/mkljczk', 'https://gts.mkljczk.pl/@mkljczk']; +const MKLJCZK_ACCOUNTS = [ + 'https://pl.fediverse.pl/users/mkljczk', + 'https://gts.mkljczk.pl/users/mkljczk', + 'https://gts.mkljczk.pl/@mkljczk', +]; const paymentOptionSchema = v.variant('type', [ v.object({ @@ -47,7 +53,9 @@ const preprocessAccount = v.transform((account: any) => { const fqn = account.fqn || guessFqn(account); const domain = fqn.split('@')[1] || ''; - const isCat = (account.pleroma?.is_cat ?? account.is_cat) || MKLJCZK_ACCOUNTS.includes(account.uri ?? account.url); + const isCat = + (account.pleroma?.is_cat ?? account.is_cat) || + MKLJCZK_ACCOUNTS.includes(account.uri ?? account.url); const speakAsCat = account.pleroma?.speak_as_cat ?? account.speak_as_cat ?? isCat; return { @@ -58,14 +66,19 @@ const preprocessAccount = v.transform((account: any) => { header: account.header || account.header_static, avatar_default: isDefaultAvatar(account.avatar || account.avatar_static || ''), header_default: isDefaultHeader(account.header || account.header_static || ''), - local: typeof account.pleroma?.is_local === 'boolean' ? account.pleroma.is_local : account.acct.split('@')[1] === undefined, + local: + typeof account.pleroma?.is_local === 'boolean' + ? account.pleroma.is_local + : account.acct.split('@')[1] === undefined, discoverable: account.discoverable || account.pleroma?.source?.discoverable, verified: account.verified || account.pleroma?.tags?.includes('verified'), - ...(account.role?.permissions ? { - is_admin: (account.role?.permissions & 0x1) === 0x1, - } : {}), + ...(account.role?.permissions + ? { + is_admin: (account.role?.permissions & 0x1) === 0x1, + } + : {}), ap_id: account.pleroma?.ap_id ?? account.actor_id, - ...(pick(account.pleroma || {}, [ + ...pick(account.pleroma || {}, [ 'background_image', 'relationship', 'is_moderator', @@ -90,21 +103,32 @@ const preprocessAccount = v.transform((account: any) => { 'notification_settings', 'location', - ])), - ...(pick(account.akkoma || {}, [ - 'permit_followback', - ])), + ]), + ...pick(account.akkoma || {}, ['permit_followback']), is_cat: isCat, speak_as_cat: speakAsCat, ...(pick(account.other_settings || {}), ['birthday', 'location']), __meta: pick(account, ['pleroma', 'source']), ...account, - display_name: account.display_name?.replace(/^[\s\u180E\u200B-\u200D\u2060\uFEFF]+|[\s\u180E\u200B-\u200D\u2060\uFEFF]+$/g, '').trim() || username, + display_name: + account.display_name + ?.replace( + /^[\s\u180E\u200B-\u200D\u2060\uFEFF]+|[\s\u180E\u200B-\u200D\u2060\uFEFF]+$/g, + '', + ) + .trim() || username, roles: account.roles?.length ? account.roles : filterBadges(account.pleroma?.tags), source: account.source - ? { ...(pick(account.pleroma?.source || {}, [ - 'show_role', 'no_rich_text', 'discoverable', 'actor_type', 'show_birthday', - ])), ...account.source } + ? { + ...pick(account.pleroma?.source || {}, [ + 'show_role', + 'no_rich_text', + 'discoverable', + 'actor_type', + 'show_birthday', + ]), + ...account.source, + } : undefined, }; }); @@ -121,7 +145,13 @@ const baseAccountSchema = v.object({ acct: v.fallback(v.string(), ''), url: v.pipe(v.string(), v.url()), display_name: v.fallback(v.string(), ''), - note: v.fallback(v.pipe(v.string(), v.transform(note => note === '

' ? '' : note)), ''), + note: v.fallback( + v.pipe( + v.string(), + v.transform((note) => (note === '

' ? '' : note)), + ), + '', + ), avatar: v.fallback(v.string(), ''), avatar_static: v.fallback(v.pipe(v.string(), v.url()), ''), header: v.fallback(v.pipe(v.string(), v.url()), ''), @@ -182,14 +212,16 @@ const baseAccountSchema = v.object({ /** The reported subscribers of this user */ subscribers_count: v.fallback(v.number(), 0), /** Identity proofs */ - identity_proofs: filteredArray(v.object({ - /** The key of a given field's key-value pair */ - name: v.fallback(v.string(), ''), - /** The value associated with the name key */ - value: v.fallback(v.string(), ''), - /** Timestamp of when the server verified the field value */ - verified_at: v.fallback(datetimeSchema, new Date().toISOString()), - })), + identity_proofs: filteredArray( + v.object({ + /** The key of a given field's key-value pair */ + name: v.fallback(v.string(), ''), + /** The value associated with the name key */ + value: v.fallback(v.string(), ''), + /** Timestamp of when the server verified the field value */ + verified_at: v.fallback(datetimeSchema, new Date().toISOString()), + }), + ), /** Payment options */ payment_options: filteredArray(paymentOptionSchema), @@ -209,7 +241,10 @@ const baseAccountSchema = v.object({ const accountWithMovedAccountSchema = v.object({ ...baseAccountSchema.entries, - moved: v.fallback(v.nullable(v.lazy((): typeof baseAccountSchema => accountWithMovedAccountSchema as any)), null), + moved: v.fallback( + v.nullable(v.lazy((): typeof baseAccountSchema => accountWithMovedAccountSchema as any)), + null, + ), }); /** @see {@link https://docs.joinmastodon.org/entities/Account/} */ @@ -229,44 +264,67 @@ type Account = v.InferOutput & WithMoved; */ const accountSchema: v.BaseSchema> = untypedAccountSchema as any; -const untypedCredentialAccountSchema = v.pipe(v.any(), preprocessAccount, v.object({ - ...accountWithMovedAccountSchema.entries, - source: v.fallback(v.nullable(coerceObject({ - attribution_domains: v.fallback(v.optional(v.nullable(v.array(v.string()))), null), - note: v.fallback(v.optional(v.string()), ''), - fields: v.fallback(v.optional(filteredArray(fieldSchema)), []), - privacy: v.fallback(v.optional(v.picklist(['public', 'unlisted', 'private', 'direct'])), 'public'), - sensitive: v.fallback(v.optional(v.boolean()), false), - language: v.fallback(v.optional(v.nullable(v.string())), null), - follow_requests_count: v.fallback(v.optional(v.pipe(v.number(), v.integer(), v.minValue(0))), 0), - hide_collections: v.fallback(v.optional(v.boolean()), undefined), - discoverable: v.fallback(v.optional(v.boolean()), undefined), - indexable: v.fallback(v.nullable(v.boolean()), null), - quote_policy: v.fallback(v.nullable(v.picklist(['public', 'followers', 'nobody'])), null), +const untypedCredentialAccountSchema = v.pipe( + v.any(), + preprocessAccount, + v.object({ + ...accountWithMovedAccountSchema.entries, + source: v.fallback( + v.nullable( + coerceObject({ + attribution_domains: v.fallback(v.optional(v.nullable(v.array(v.string()))), null), + note: v.fallback(v.optional(v.string()), ''), + fields: v.fallback(v.optional(filteredArray(fieldSchema)), []), + privacy: v.fallback( + v.optional(v.picklist(['public', 'unlisted', 'private', 'direct'])), + 'public', + ), + sensitive: v.fallback(v.optional(v.boolean()), false), + language: v.fallback(v.optional(v.nullable(v.string())), null), + follow_requests_count: v.fallback( + v.optional(v.pipe(v.number(), v.integer(), v.minValue(0))), + 0, + ), + hide_collections: v.fallback(v.optional(v.boolean()), undefined), + discoverable: v.fallback(v.optional(v.boolean()), undefined), + indexable: v.fallback(v.nullable(v.boolean()), null), + quote_policy: v.fallback(v.nullable(v.picklist(['public', 'followers', 'nobody'])), null), - show_role: v.fallback(v.optional(v.nullable(v.boolean())), undefined), - no_rich_text: v.fallback(v.optional(v.nullable(v.boolean())), undefined), - actor_type: v.fallback(v.optional(v.string()), undefined), - show_birthday: v.fallback(v.optional(v.boolean()), undefined), + show_role: v.fallback(v.optional(v.nullable(v.boolean())), undefined), + no_rich_text: v.fallback(v.optional(v.nullable(v.boolean())), undefined), + actor_type: v.fallback(v.optional(v.string()), undefined), + show_birthday: v.fallback(v.optional(v.boolean()), undefined), - also_known_as_uris: v.fallback(v.optional(v.array(v.string())), undefined), - status_content_type: v.fallback(v.optional(v.string()), undefined), - web_include_boosts: v.fallback(v.optional(v.boolean()), undefined), - web_layout: v.fallback(v.optional(v.picklist(['microblog', 'gallery'])), undefined), - web_visibility: v.fallback(v.optional(v.picklist(['public', 'unlisted', 'none'])), undefined), - })), null), - role: v.fallback(v.nullable(roleSchema), null), + also_known_as_uris: v.fallback(v.optional(v.array(v.string())), undefined), + status_content_type: v.fallback(v.optional(v.string()), undefined), + web_include_boosts: v.fallback(v.optional(v.boolean()), undefined), + web_layout: v.fallback(v.optional(v.picklist(['microblog', 'gallery'])), undefined), + web_visibility: v.fallback( + v.optional(v.picklist(['public', 'unlisted', 'none'])), + undefined, + ), + }), + ), + null, + ), + role: v.fallback(v.nullable(roleSchema), null), - settings_store: v.fallback(v.optional(v.record(v.string(), v.any())), undefined), - chat_token: v.fallback(v.optional(v.string()), undefined), - allow_following_move: v.fallback(v.optional(v.boolean()), undefined), - unread_conversation_count: v.fallback(v.optional(v.number()), undefined), - unread_notifications_count: v.fallback(v.optional(v.number()), undefined), - notification_settings: v.fallback(v.optional(v.object({ - block_from_strangers: v.fallback(v.boolean(), false), - hide_notification_contents: v.fallback(v.boolean(), false), - })), undefined), -})); + settings_store: v.fallback(v.optional(v.record(v.string(), v.any())), undefined), + chat_token: v.fallback(v.optional(v.string()), undefined), + allow_following_move: v.fallback(v.optional(v.boolean()), undefined), + unread_conversation_count: v.fallback(v.optional(v.number()), undefined), + unread_notifications_count: v.fallback(v.optional(v.number()), undefined), + notification_settings: v.fallback( + v.optional( + v.object({ + block_from_strangers: v.fallback(v.boolean(), false), + hide_notification_contents: v.fallback(v.boolean(), false), + }), + ), + undefined, + ), + }), +); /** * @category Entity types @@ -276,12 +334,20 @@ type CredentialAccount = v.InferOutput & /** * @category Schemas */ -const credentialAccountSchema: v.BaseSchema> = untypedCredentialAccountSchema as any; +const credentialAccountSchema: v.BaseSchema< + any, + CredentialAccount, + v.BaseIssue +> = untypedCredentialAccountSchema as any; -const untypedBlockedAccountSchema = v.pipe(v.any(), preprocessAccount, v.object({ - ...accountWithMovedAccountSchema.entries, - block_expires_at: v.fallback(v.nullable(datetimeSchema), null), -})); +const untypedBlockedAccountSchema = v.pipe( + v.any(), + preprocessAccount, + v.object({ + ...accountWithMovedAccountSchema.entries, + block_expires_at: v.fallback(v.nullable(datetimeSchema), null), + }), +); /** * @category Entity types @@ -291,12 +357,20 @@ type BlockedAccount = v.InferOutput & WithMo /** * @category Schemas */ -const blockedAccountSchema: v.BaseSchema> = untypedBlockedAccountSchema as any; +const blockedAccountSchema: v.BaseSchema< + any, + BlockedAccount, + v.BaseIssue +> = untypedBlockedAccountSchema as any; -const untypedMutedAccountSchema = v.pipe(v.any(), preprocessAccount, v.object({ - ...accountWithMovedAccountSchema.entries, - mute_expires_at: v.fallback(v.nullable(datetimeSchema), null), -})); +const untypedMutedAccountSchema = v.pipe( + v.any(), + preprocessAccount, + v.object({ + ...accountWithMovedAccountSchema.entries, + mute_expires_at: v.fallback(v.nullable(datetimeSchema), null), + }), +); /** * @category Entity types @@ -306,7 +380,11 @@ type MutedAccount = v.InferOutput & WithMoved; /** * @category Schemas */ -const mutedAccountSchema: v.BaseSchema> = untypedMutedAccountSchema as any; +const mutedAccountSchema: v.BaseSchema< + any, + MutedAccount, + v.BaseIssue +> = untypedMutedAccountSchema as any; export { accountSchema, diff --git a/packages/pl-api/lib/entities/admin/account.ts b/packages/pl-api/lib/entities/admin/account.ts index acff02537..0914a672f 100644 --- a/packages/pl-api/lib/entities/admin/account.ts +++ b/packages/pl-api/lib/entities/admin/account.ts @@ -14,10 +14,10 @@ const adminAccountSchema = v.pipe( v.any(), v.transform((account: any) => { if (!account.account) { - /** - * Convert Pleroma account schema - * @see {@link https://docs.pleroma.social/backend/development/API/admin_api/#get-apiv1pleromaadminusers} - */ + /** + * Convert Pleroma account schema + * @see {@link https://docs.pleroma.social/backend/development/API/admin_api/#get-apiv1pleromaadminusers} + */ return { id: account.id, account: null, @@ -29,8 +29,8 @@ const adminAccountSchema = v.pipe( role: account.roles?.is_admin ? v.parse(roleSchema, { name: 'Admin' }) : account.roles?.moderator - ? v.parse(roleSchema, { name: 'Moderator ' }) : - null, + ? v.parse(roleSchema, { name: 'Moderator ' }) + : null, confirmed: account.is_confirmed, approved: account.is_approved, disabled: !account.is_active, @@ -74,7 +74,4 @@ const adminAccountSchema = v.pipe( */ type AdminAccount = v.InferOutput; -export { - adminAccountSchema, - type AdminAccount, -}; +export { adminAccountSchema, type AdminAccount }; diff --git a/packages/pl-api/lib/entities/admin/canonical-email-block.ts b/packages/pl-api/lib/entities/admin/canonical-email-block.ts index 1ed511266..2f15fce62 100644 --- a/packages/pl-api/lib/entities/admin/canonical-email-block.ts +++ b/packages/pl-api/lib/entities/admin/canonical-email-block.ts @@ -14,7 +14,4 @@ const adminCanonicalEmailBlockSchema = v.object({ */ type AdminCanonicalEmailBlock = v.InferOutput; -export { - adminCanonicalEmailBlockSchema, - type AdminCanonicalEmailBlock, -}; +export { adminCanonicalEmailBlockSchema, type AdminCanonicalEmailBlock }; diff --git a/packages/pl-api/lib/entities/admin/cohort.ts b/packages/pl-api/lib/entities/admin/cohort.ts index 824f240e9..e933a7ffb 100644 --- a/packages/pl-api/lib/entities/admin/cohort.ts +++ b/packages/pl-api/lib/entities/admin/cohort.ts @@ -9,11 +9,13 @@ import { datetimeSchema } from '../utils'; const adminCohortSchema = v.object({ period: datetimeSchema, frequency: v.picklist(['day', 'month']), - data: v.array(v.object({ - date: datetimeSchema, - rate: v.number(), - value: v.pipe(v.unknown(), v.transform(Number)), - })), + data: v.array( + v.object({ + date: datetimeSchema, + rate: v.number(), + value: v.pipe(v.unknown(), v.transform(Number)), + }), + ), }); /** @@ -21,7 +23,4 @@ const adminCohortSchema = v.object({ */ type AdminCohort = v.InferOutput; -export { - adminCohortSchema, - type AdminCohort, -}; +export { adminCohortSchema, type AdminCohort }; diff --git a/packages/pl-api/lib/entities/admin/dimension.ts b/packages/pl-api/lib/entities/admin/dimension.ts index 4682bcc09..7eab9c52a 100644 --- a/packages/pl-api/lib/entities/admin/dimension.ts +++ b/packages/pl-api/lib/entities/admin/dimension.ts @@ -6,13 +6,15 @@ import * as v from 'valibot'; */ const adminDimensionSchema = v.object({ key: v.string(), - data: v.array(v.object({ - key: v.string(), - human_key: v.string(), - value: v.string(), - unit: v.fallback(v.optional(v.string()), undefined), - human_value: v.fallback(v.optional(v.string()), undefined), - })), + data: v.array( + v.object({ + key: v.string(), + human_key: v.string(), + value: v.string(), + unit: v.fallback(v.optional(v.string()), undefined), + human_value: v.fallback(v.optional(v.string()), undefined), + }), + ), }); /** @@ -20,7 +22,4 @@ const adminDimensionSchema = v.object({ */ type AdminDimension = v.InferOutput; -export { - adminDimensionSchema, - type AdminDimension, -}; +export { adminDimensionSchema, type AdminDimension }; diff --git a/packages/pl-api/lib/entities/admin/domain-allow.ts b/packages/pl-api/lib/entities/admin/domain-allow.ts index 109500052..3c1c5f9d8 100644 --- a/packages/pl-api/lib/entities/admin/domain-allow.ts +++ b/packages/pl-api/lib/entities/admin/domain-allow.ts @@ -17,7 +17,4 @@ const adminDomainAllowSchema = v.object({ */ type AdminDomainAllow = v.InferOutput; -export { - adminDomainAllowSchema, - type AdminDomainAllow, -}; +export { adminDomainAllowSchema, type AdminDomainAllow }; diff --git a/packages/pl-api/lib/entities/admin/domain-block.ts b/packages/pl-api/lib/entities/admin/domain-block.ts index 0a79df86d..ac557004e 100644 --- a/packages/pl-api/lib/entities/admin/domain-block.ts +++ b/packages/pl-api/lib/entities/admin/domain-block.ts @@ -24,7 +24,4 @@ const adminDomainBlockSchema = v.object({ */ type AdminDomainBlock = v.InferOutput; -export { - adminDomainBlockSchema, - type AdminDomainBlock, -}; +export { adminDomainBlockSchema, type AdminDomainBlock }; diff --git a/packages/pl-api/lib/entities/admin/domain.ts b/packages/pl-api/lib/entities/admin/domain.ts index 4a6da0611..e0ead48cf 100644 --- a/packages/pl-api/lib/entities/admin/domain.ts +++ b/packages/pl-api/lib/entities/admin/domain.ts @@ -16,6 +16,6 @@ const adminDomainSchema = v.object({ /** * @category Admin entity types */ -type AdminDomain = v.InferOutput +type AdminDomain = v.InferOutput; export { adminDomainSchema, type AdminDomain }; diff --git a/packages/pl-api/lib/entities/admin/email-domain-block.ts b/packages/pl-api/lib/entities/admin/email-domain-block.ts index f731574be..1c3587b56 100644 --- a/packages/pl-api/lib/entities/admin/email-domain-block.ts +++ b/packages/pl-api/lib/entities/admin/email-domain-block.ts @@ -10,11 +10,13 @@ const adminEmailDomainBlockSchema = v.object({ id: v.string(), domain: v.string(), created_at: datetimeSchema, - history: v.array(v.object({ - day: v.pipe(v.unknown(), v.transform(String)), - accounts: v.pipe(v.unknown(), v.transform(String)), - uses: v.pipe(v.unknown(), v.transform(String)), - })), + history: v.array( + v.object({ + day: v.pipe(v.unknown(), v.transform(String)), + accounts: v.pipe(v.unknown(), v.transform(String)), + uses: v.pipe(v.unknown(), v.transform(String)), + }), + ), }); /** @@ -22,7 +24,4 @@ const adminEmailDomainBlockSchema = v.object({ */ type AdminEmailDomainBlock = v.InferOutput; -export { - adminEmailDomainBlockSchema, - type AdminEmailDomainBlock, -}; +export { adminEmailDomainBlockSchema, type AdminEmailDomainBlock }; diff --git a/packages/pl-api/lib/entities/admin/ip-block.ts b/packages/pl-api/lib/entities/admin/ip-block.ts index b76bd685f..aa0161a9f 100644 --- a/packages/pl-api/lib/entities/admin/ip-block.ts +++ b/packages/pl-api/lib/entities/admin/ip-block.ts @@ -20,7 +20,4 @@ const adminIpBlockSchema = v.object({ */ type AdminIpBlock = v.InferOutput; -export { - adminIpBlockSchema, - type AdminIpBlock, -}; +export { adminIpBlockSchema, type AdminIpBlock }; diff --git a/packages/pl-api/lib/entities/admin/ip.ts b/packages/pl-api/lib/entities/admin/ip.ts index 1a323a144..d8825de71 100644 --- a/packages/pl-api/lib/entities/admin/ip.ts +++ b/packages/pl-api/lib/entities/admin/ip.ts @@ -16,7 +16,4 @@ const adminIpSchema = v.object({ */ type AdminIp = v.InferOutput; -export { - adminIpSchema, - type AdminIp, -}; +export { adminIpSchema, type AdminIp }; diff --git a/packages/pl-api/lib/entities/admin/measure.ts b/packages/pl-api/lib/entities/admin/measure.ts index 95d69fb2f..0af3a7258 100644 --- a/packages/pl-api/lib/entities/admin/measure.ts +++ b/packages/pl-api/lib/entities/admin/measure.ts @@ -12,10 +12,12 @@ const adminMeasureSchema = v.object({ total: v.pipe(v.unknown(), v.transform(Number)), human_value: v.fallback(v.optional(v.string()), undefined), previous_total: v.fallback(v.optional(v.pipe(v.unknown(), v.transform(Number))), undefined), - data: v.array(v.object({ - date: dateSchema, - value: v.pipe(v.unknown(), v.transform(Number)), - })), + data: v.array( + v.object({ + date: dateSchema, + value: v.pipe(v.unknown(), v.transform(Number)), + }), + ), }); /** @@ -23,7 +25,4 @@ const adminMeasureSchema = v.object({ */ type AdminMeasure = v.InferOutput; -export { - adminMeasureSchema, - type AdminMeasure, -}; +export { adminMeasureSchema, type AdminMeasure }; diff --git a/packages/pl-api/lib/entities/admin/moderation-log-entry.ts b/packages/pl-api/lib/entities/admin/moderation-log-entry.ts index 5c36c6b1e..5b33d6339 100644 --- a/packages/pl-api/lib/entities/admin/moderation-log-entry.ts +++ b/packages/pl-api/lib/entities/admin/moderation-log-entry.ts @@ -14,6 +14,6 @@ const adminModerationLogEntrySchema = v.object({ /** * @category Admin entity types */ -type AdminModerationLogEntry = v.InferOutput +type AdminModerationLogEntry = v.InferOutput; export { adminModerationLogEntrySchema, type AdminModerationLogEntry }; diff --git a/packages/pl-api/lib/entities/admin/pleroma-config.ts b/packages/pl-api/lib/entities/admin/pleroma-config.ts index 8621dc3da..7b0876ac6 100644 --- a/packages/pl-api/lib/entities/admin/pleroma-config.ts +++ b/packages/pl-api/lib/entities/admin/pleroma-config.ts @@ -4,17 +4,19 @@ import * as v from 'valibot'; * @category Admin schemas */ const pleromaConfigSchema = v.object({ - configs: v.array(v.object({ - value: v.any(), - group: v.string(), - key: v.string(), - })), + configs: v.array( + v.object({ + value: v.any(), + group: v.string(), + key: v.string(), + }), + ), need_reboot: v.boolean(), }); /** * @category Admin entity types */ -type PleromaConfig = v.InferOutput +type PleromaConfig = v.InferOutput; export { pleromaConfigSchema, type PleromaConfig }; diff --git a/packages/pl-api/lib/entities/admin/relay.ts b/packages/pl-api/lib/entities/admin/relay.ts index c2b5ded52..4cfd9ede7 100644 --- a/packages/pl-api/lib/entities/admin/relay.ts +++ b/packages/pl-api/lib/entities/admin/relay.ts @@ -16,6 +16,6 @@ const adminRelaySchema = v.pipe( /** * @category Admin entity types */ -type AdminRelay = v.InferOutput +type AdminRelay = v.InferOutput; export { adminRelaySchema, type AdminRelay }; diff --git a/packages/pl-api/lib/entities/admin/report.ts b/packages/pl-api/lib/entities/admin/report.ts index dffe682fc..fc769b6e5 100644 --- a/packages/pl-api/lib/entities/admin/report.ts +++ b/packages/pl-api/lib/entities/admin/report.ts @@ -15,17 +15,17 @@ const adminReportSchema = v.pipe( v.any(), v.transform((report: any) => { if (report.actor) { - /** - * Convert Pleroma report schema - * @see {@link https://docs.pleroma.social/backend/development/API/admin_api/#get-apiv1pleromaadminreports} - */ + /** + * Convert Pleroma report schema + * @see {@link https://docs.pleroma.social/backend/development/API/admin_api/#get-apiv1pleromaadminreports} + */ return { action_taken: report.state !== 'open', comment: report.content, updated_at: report.created_at, account: report.actor, target_account: report.account, - ...(pick(report, ['id', 'assigned_account', 'created_at', 'rules', 'statuses'])), + ...pick(report, ['id', 'assigned_account', 'created_at', 'rules', 'statuses']), }; } return report; diff --git a/packages/pl-api/lib/entities/admin/tag.ts b/packages/pl-api/lib/entities/admin/tag.ts index e4320fc01..9b36749db 100644 --- a/packages/pl-api/lib/entities/admin/tag.ts +++ b/packages/pl-api/lib/entities/admin/tag.ts @@ -19,7 +19,4 @@ const adminTagSchema = v.object({ */ type AdminTag = v.InferOutput; -export { - adminTagSchema, - type AdminTag, -}; +export { adminTagSchema, type AdminTag }; diff --git a/packages/pl-api/lib/entities/announcement.ts b/packages/pl-api/lib/entities/announcement.ts index 683aba701..a81b92eb1 100644 --- a/packages/pl-api/lib/entities/announcement.ts +++ b/packages/pl-api/lib/entities/announcement.ts @@ -21,9 +21,13 @@ const announcementSchema = v.object({ reactions: filteredArray(announcementReactionSchema), statuses: v.pipe( v.any(), - v.transform((statuses: any) => Array.isArray(statuses) - ? Object.fromEntries(statuses.map((status: any) => [status.url, status.account?.acct]) || []) - : statuses), + v.transform((statuses: any) => + Array.isArray(statuses) + ? Object.fromEntries( + statuses.map((status: any) => [status.url, status.account?.acct]) || [], + ) + : statuses, + ), v.record(v.string(), v.string()), ), mentions: filteredArray(mentionSchema), diff --git a/packages/pl-api/lib/entities/antenna.ts b/packages/pl-api/lib/entities/antenna.ts index b51e3bc3f..992fbea47 100644 --- a/packages/pl-api/lib/entities/antenna.ts +++ b/packages/pl-api/lib/entities/antenna.ts @@ -30,6 +30,6 @@ const antennaSchema: v.BaseSchema> = v.object */ type Antenna = v.InferOutput & { list: List | null; -} +}; export { antennaSchema, type Antenna }; diff --git a/packages/pl-api/lib/entities/application.ts b/packages/pl-api/lib/entities/application.ts index ad40c1dad..0fd4533a9 100644 --- a/packages/pl-api/lib/entities/application.ts +++ b/packages/pl-api/lib/entities/application.ts @@ -6,21 +6,25 @@ import { filteredArray } from './utils'; * @category Schemas * @see {@link https://docs.joinmastodon.org/entities/Application/} */ -const applicationSchema = v.pipe(v.any(), v.transform((application) => ({ - redirect_uris: [application.redirect_uri], - ...application, -})), v.object({ - name: v.fallback(v.string(), ''), - website: v.fallback(v.optional(v.string()), undefined), - redirect_uris: filteredArray(v.string()), +const applicationSchema = v.pipe( + v.any(), + v.transform((application) => ({ + redirect_uris: [application.redirect_uri], + ...application, + })), + v.object({ + name: v.fallback(v.string(), ''), + website: v.fallback(v.optional(v.string()), undefined), + redirect_uris: filteredArray(v.string()), - id: v.fallback(v.optional(v.string()), undefined), + id: v.fallback(v.optional(v.string()), undefined), - /** @deprecated */ - redirect_uri: v.fallback(v.optional(v.string()), undefined), - /** @deprecated */ - vapid_key: v.fallback(v.optional(v.string()), undefined), -})); + /** @deprecated */ + redirect_uri: v.fallback(v.optional(v.string()), undefined), + /** @deprecated */ + vapid_key: v.fallback(v.optional(v.string()), undefined), + }), +); type Application = v.InferOutput; @@ -35,7 +39,10 @@ const credentialApplicationSchema = v.pipe( ...applicationSchema.pipe[2].entries, client_id: v.string(), client_secret: v.string(), - client_secret_expires_at: v.fallback(v.nullable(v.pipe(v.unknown(), v.transform(Number))), null), + client_secret_expires_at: v.fallback( + v.nullable(v.pipe(v.unknown(), v.transform(Number))), + null, + ), }), ); @@ -44,4 +51,9 @@ const credentialApplicationSchema = v.pipe( */ type CredentialApplication = v.InferOutput; -export { applicationSchema, credentialApplicationSchema, type Application, type CredentialApplication }; +export { + applicationSchema, + credentialApplicationSchema, + type Application, + type CredentialApplication, +}; diff --git a/packages/pl-api/lib/entities/bookmark-folder.ts b/packages/pl-api/lib/entities/bookmark-folder.ts index 77dc5fd75..681ad1b6f 100644 --- a/packages/pl-api/lib/entities/bookmark-folder.ts +++ b/packages/pl-api/lib/entities/bookmark-folder.ts @@ -3,15 +3,19 @@ import * as v from 'valibot'; /** * @category Schemas */ -const bookmarkFolderSchema = v.pipe(v.any(), v.transform((data) => ({ - name: data.title, - ...data, -})), v.object({ - id: v.pipe(v.unknown(), v.transform(String)), - name: v.fallback(v.string(), ''), - emoji: v.fallback(v.nullable(v.string()), null), - emoji_url: v.fallback(v.nullable(v.string()), null), -})); +const bookmarkFolderSchema = v.pipe( + v.any(), + v.transform((data) => ({ + name: data.title, + ...data, + })), + v.object({ + id: v.pipe(v.unknown(), v.transform(String)), + name: v.fallback(v.string(), ''), + emoji: v.fallback(v.nullable(v.string()), null), + emoji_url: v.fallback(v.nullable(v.string()), null), + }), +); /** * @category Entity types diff --git a/packages/pl-api/lib/entities/drive-file.ts b/packages/pl-api/lib/entities/drive-file.ts index 9409adad9..f7126d527 100644 --- a/packages/pl-api/lib/entities/drive-file.ts +++ b/packages/pl-api/lib/entities/drive-file.ts @@ -2,24 +2,28 @@ import * as v from 'valibot'; /** * @category Schemas -*/ -const driveFileSchema = v.pipe(v.any(), v.transform((file) => ({ - ...file, - thumbnail_url: file.thumbnailUrl, - content_type: file.contentType, - is_avatar: file.isAvatar, - is_banner: file.isBanner, -})), v.object({ - id: v.string(), - url: v.string(), - thumbnail_url: v.string(), - filename: v.string(), - content_type: v.string(), - sensitive: v.boolean(), - description: v.fallback(v.nullable(v.string()), null), - is_avatar: v.boolean(), - is_banner: v.boolean(), -})); + */ +const driveFileSchema = v.pipe( + v.any(), + v.transform((file) => ({ + ...file, + thumbnail_url: file.thumbnailUrl, + content_type: file.contentType, + is_avatar: file.isAvatar, + is_banner: file.isBanner, + })), + v.object({ + id: v.string(), + url: v.string(), + thumbnail_url: v.string(), + filename: v.string(), + content_type: v.string(), + sensitive: v.boolean(), + description: v.fallback(v.nullable(v.string()), null), + is_avatar: v.boolean(), + is_banner: v.boolean(), + }), +); /** * @category Entity types diff --git a/packages/pl-api/lib/entities/drive-folder.ts b/packages/pl-api/lib/entities/drive-folder.ts index db6e85961..853a84310 100644 --- a/packages/pl-api/lib/entities/drive-folder.ts +++ b/packages/pl-api/lib/entities/drive-folder.ts @@ -19,18 +19,22 @@ const baseDriveFolderSchema = v.object({ /** * @category Schemas -*/ -const driveFolderSchema: v.BaseSchema> = v.pipe(v.any(), v.transform((folder) => ({ - ...folder, - parent_id: folder.parentId, - path: folder.path?.map((entry: any) => ({ - ...entry, - parent_id: entry.parentId, + */ +const driveFolderSchema: v.BaseSchema> = v.pipe( + v.any(), + v.transform((folder) => ({ + ...folder, + parent_id: folder.parentId, + path: folder.path?.map((entry: any) => ({ + ...entry, + parent_id: entry.parentId, + })), })), -})), v.object({ - ...baseDriveFolderSchema.entries, - folders: filteredArray(v.lazy(() => driveFolderSchema)), -})) as any; + v.object({ + ...baseDriveFolderSchema.entries, + folders: filteredArray(v.lazy(() => driveFolderSchema)), + }), +) as any; /** * @category Entity types @@ -41,6 +45,6 @@ type DriveFolder = { parent_id: string | null; files: Array; folders: Array; -} +}; export { driveFolderSchema, type DriveFolder }; diff --git a/packages/pl-api/lib/entities/drive-status.ts b/packages/pl-api/lib/entities/drive-status.ts index f711708de..7f2ecfe19 100644 --- a/packages/pl-api/lib/entities/drive-status.ts +++ b/packages/pl-api/lib/entities/drive-status.ts @@ -2,15 +2,19 @@ import * as v from 'valibot'; /** * @category Schemas -*/ -const driveStatusSchema = v.pipe(v.any(), v.transform((status) => ({ - file_count: status.fileCount, - used_size: status.usedSize, - ...status, -})), v.object({ - file_count: v.fallback(v.number(), 0), - used_size: v.fallback(v.number(), 0), -})); + */ +const driveStatusSchema = v.pipe( + v.any(), + v.transform((status) => ({ + file_count: status.fileCount, + used_size: status.usedSize, + ...status, + })), + v.object({ + file_count: v.fallback(v.number(), 0), + used_size: v.fallback(v.number(), 0), + }), +); /** * @category Entity types diff --git a/packages/pl-api/lib/entities/emoji-reaction.ts b/packages/pl-api/lib/entities/emoji-reaction.ts index 058a27904..1c8e96ccc 100644 --- a/packages/pl-api/lib/entities/emoji-reaction.ts +++ b/packages/pl-api/lib/entities/emoji-reaction.ts @@ -25,15 +25,19 @@ const customEmojiReactionSchema = v.object({ * * @category Schemas * @see {@link https://docs.pleroma.social/backend/development/API/differences_in_mastoapi_responses/#statuses} -*/ + */ const emojiReactionSchema = v.pipe( v.any(), - v.transform((reaction: any) => reaction ? { - url: reaction.url, - static_url: reaction.url, - account_ids: reaction.accounts?.map((account: any) => account?.id), - ...reaction, - } : null), + v.transform((reaction: any) => + reaction + ? { + url: reaction.url, + static_url: reaction.url, + account_ids: reaction.accounts?.map((account: any) => account?.id), + ...reaction, + } + : null, + ), v.union([baseEmojiReactionSchema, customEmojiReactionSchema]), ); diff --git a/packages/pl-api/lib/entities/familiar-followers.ts b/packages/pl-api/lib/entities/familiar-followers.ts index 519f9fe48..0f9856c93 100644 --- a/packages/pl-api/lib/entities/familiar-followers.ts +++ b/packages/pl-api/lib/entities/familiar-followers.ts @@ -15,6 +15,6 @@ const familiarFollowersSchema = v.object({ /** * @category Entity types */ -type FamiliarFollowers = v.InferOutput +type FamiliarFollowers = v.InferOutput; export { familiarFollowersSchema, type FamiliarFollowers }; diff --git a/packages/pl-api/lib/entities/filter.ts b/packages/pl-api/lib/entities/filter.ts index 7314ed755..b54737f7f 100644 --- a/packages/pl-api/lib/entities/filter.ts +++ b/packages/pl-api/lib/entities/filter.ts @@ -42,11 +42,13 @@ const filterSchema = v.pipe( return { ...filter, title: filter.phrase, - keywords: [{ - id: '1', - keyword: filter.phrase, - whole_word: filter.whole_word, - }], + keywords: [ + { + id: '1', + keyword: filter.phrase, + whole_word: filter.whole_word, + }, + ], filter_action: filter.irreversible ? 'hide' : 'warn', }; } diff --git a/packages/pl-api/lib/entities/group-member.ts b/packages/pl-api/lib/entities/group-member.ts index 026a87cb2..1ec0f99c2 100644 --- a/packages/pl-api/lib/entities/group-member.ts +++ b/packages/pl-api/lib/entities/group-member.ts @@ -5,33 +5,38 @@ import { accountSchema } from './account'; enum GroupRoles { OWNER = 'owner', ADMIN = 'admin', - USER = 'user' + USER = 'user', } /** * @category Entity types */ -type GroupRole =`${GroupRoles}`; +type GroupRole = `${GroupRoles}`; /** * @category Schemas */ -const groupMemberSchema = v.pipe(v.any(), v.transform((groupMember: any) => { - if (!groupMember.account) { - return { - id: groupMember.id, - account: groupMember, - role: { - founder: 'owner', - admin: 'admin', - }[groupMember.role as string] || 'user', - }; - } -}), v.object({ - id: v.string(), - account: accountSchema, - role: v.enum(GroupRoles), -})); +const groupMemberSchema = v.pipe( + v.any(), + v.transform((groupMember: any) => { + if (!groupMember.account) { + return { + id: groupMember.id, + account: groupMember, + role: + { + founder: 'owner', + admin: 'admin', + }[groupMember.role as string] || 'user', + }; + } + }), + v.object({ + id: v.string(), + account: accountSchema, + role: v.enum(GroupRoles), + }), +); /** * @category Entity types diff --git a/packages/pl-api/lib/entities/group.ts b/packages/pl-api/lib/entities/group.ts index a46d8369a..7bb1317e0 100644 --- a/packages/pl-api/lib/entities/group.ts +++ b/packages/pl-api/lib/entities/group.ts @@ -10,61 +10,74 @@ import { datetimeSchema, filteredArray } from './utils'; /** * @category Schemas */ -const groupSchema = v.pipe(v.any(), v.transform((group: any) => { - const domain = getDomainFromURL(group); +const groupSchema = v.pipe( + v.any(), + v.transform((group: any) => { + const domain = getDomainFromURL(group); - if (group?.config) { - group = { - display_name: group.name, - members_count: group.member_count, - note: group.short_description, - relationship: group.self ? { - ...group.self, - member: group.self.is_member, - role: { - founder: 'owner', - admin: 'admin', - }[group.self.role as string] || 'user', - id: group.id, - } : null, + if (group?.config) { + group = { + display_name: group.name, + members_count: group.member_count, + note: group.short_description, + relationship: group.self + ? { + ...group.self, + member: group.self.is_member, + role: + { + founder: 'owner', + admin: 'admin', + }[group.self.role as string] || 'user', + id: group.id, + } + : null, + ...group, + }; + } + + return { + domain, ...group, + avatar: group.avatar || group.avatar_static, + header: group.header || group.header_static, + avatar_default: isDefaultAvatar(group.avatar || group.avatar_static || ''), + header_default: isDefaultHeader(group.header || group.header_static || ''), }; - } + }), + v.object({ + avatar: v.fallback(v.string(), ''), + avatar_static: v.fallback(v.string(), ''), + created_at: v.fallback(datetimeSchema, new Date().toISOString()), + display_name: v.fallback(v.string(), ''), + domain: v.fallback(v.string(), ''), + emojis: filteredArray(customEmojiSchema), + header: v.fallback(v.string(), ''), + header_static: v.fallback(v.string(), ''), + id: v.pipe(v.unknown(), v.transform(String)), + locked: v.fallback(v.boolean(), false), + membership_required: v.fallback(v.boolean(), false), + members_count: v.fallback(v.number(), 0), + owner: v.fallback(v.nullable(v.object({ id: v.string() })), null), + note: v.fallback( + v.pipe( + v.string(), + v.transform((note) => (note === '

' ? '' : note)), + ), + '', + ), + relationship: v.fallback(v.nullable(groupRelationshipSchema), null), + statuses_visibility: v.fallback(v.string(), 'public'), + uri: v.fallback(v.string(), ''), + url: v.fallback(v.string(), ''), - return { - domain, - ...group, - avatar: group.avatar || group.avatar_static, - header: group.header || group.header_static, - avatar_default: isDefaultAvatar(group.avatar || group.avatar_static || ''), - header_default: isDefaultHeader(group.header || group.header_static || ''), - }; -}), v.object({ - avatar: v.fallback(v.string(), ''), - avatar_static: v.fallback(v.string(), ''), - created_at: v.fallback(datetimeSchema, new Date().toISOString()), - display_name: v.fallback(v.string(), ''), - domain: v.fallback(v.string(), ''), - emojis: filteredArray(customEmojiSchema), - header: v.fallback(v.string(), ''), - header_static: v.fallback(v.string(), ''), - id: v.pipe(v.unknown(), v.transform(String)), - locked: v.fallback(v.boolean(), false), - membership_required: v.fallback(v.boolean(), false), - members_count: v.fallback(v.number(), 0), - owner: v.fallback(v.nullable(v.object({ id: v.string() })), null), - note: v.fallback(v.pipe(v.string(), v.transform(note => note === '

' ? '' : note)), ''), - relationship: v.fallback(v.nullable(groupRelationshipSchema), null), - statuses_visibility: v.fallback(v.string(), 'public'), - uri: v.fallback(v.string(), ''), - url: v.fallback(v.string(), ''), + avatar_description: v.fallback(v.string(), ''), + header_description: v.fallback(v.string(), ''), - avatar_description: v.fallback(v.string(), ''), - header_description: v.fallback(v.string(), ''), - - avatar_default: v.fallback(v.boolean(), false), - header_default: v.fallback(v.boolean(), false), -})); + avatar_default: v.fallback(v.boolean(), false), + header_default: v.fallback(v.boolean(), false), + }), +); /** * @category Entity types diff --git a/packages/pl-api/lib/entities/grouped-notifications-results.ts b/packages/pl-api/lib/entities/grouped-notifications-results.ts index 702f2618d..5d51c6c19 100644 --- a/packages/pl-api/lib/entities/grouped-notifications-results.ts +++ b/packages/pl-api/lib/entities/grouped-notifications-results.ts @@ -46,7 +46,16 @@ const mentionNotificationGroupSchema = v.object({ const statusNotificationGroupSchema = v.object({ ...baseNotificationGroupSchema.entries, - type: v.picklist(['status', 'reblog', 'favourite', 'poll', 'update', 'event_reminder', 'quote', 'quoted_update']), + type: v.picklist([ + 'status', + 'reblog', + 'favourite', + 'poll', + 'update', + 'event_reminder', + 'quote', + 'quoted_update', + ]), status_id: v.string(), }); @@ -105,11 +114,12 @@ const notificationGroupSchema: v.BaseSchema; +>; /** * @category Schemas @@ -156,4 +167,9 @@ const groupedNotificationsResultsSchema = v.object({ */ type GroupedNotificationsResults = v.InferOutput; -export { notificationGroupSchema, groupedNotificationsResultsSchema, type NotificationGroup, type GroupedNotificationsResults }; +export { + notificationGroupSchema, + groupedNotificationsResultsSchema, + type NotificationGroup, + type GroupedNotificationsResults, +}; diff --git a/packages/pl-api/lib/entities/instance.ts b/packages/pl-api/lib/entities/instance.ts index 93bdd0ddd..6de041054 100644 --- a/packages/pl-api/lib/entities/instance.ts +++ b/packages/pl-api/lib/entities/instance.ts @@ -8,14 +8,19 @@ const SNAC_REGEX = /^([0-9.]*) \(not true; really snac\/([\w.]*)\)/; const WORDPRESS_REGEX = /^WordPress\/[\w+.-]*, EMA\/([\w+.-]*)/; const getApiVersions = (instance: any): Record => ({ - ...Object.fromEntries(instance.pleroma?.metadata?.features?.map((feature: string) => { - let string = `${feature}.pleroma.pl-api`; - if (string.startsWith('pleroma:') || string.startsWith('pleroma_')) string = string.slice(8); - if (string.startsWith('akkoma:')) string = string.slice(7); - if (string.startsWith('pl:')) string = string.slice(3); - return [string, 1]; - }) || []), - ...Object.fromEntries(instance.fedibird_capabilities?.map((feature: string) => [`${feature}.fedibird.pl-api`, 1]) || []), + ...Object.fromEntries( + instance.pleroma?.metadata?.features?.map((feature: string) => { + let string = `${feature}.pleroma.pl-api`; + if (string.startsWith('pleroma:') || string.startsWith('pleroma_')) string = string.slice(8); + if (string.startsWith('akkoma:')) string = string.slice(7); + if (string.startsWith('pl:')) string = string.slice(3); + return [string, 1]; + }) || [], + ), + ...Object.fromEntries( + instance.fedibird_capabilities?.map((feature: string) => [`${feature}.fedibird.pl-api`, 1]) || + [], + ), ...instance.api_versions, }); @@ -49,7 +54,7 @@ const instanceV1ToV2 = (data: any) => { return { ...instance, - account_domain: instance.account_domain || uri, + account_domain: instance.account_domain || uri, configuration: { ...configuration, media_attachments: { @@ -59,7 +64,8 @@ const instanceV1ToV2 = (data: any) => { }, polls: { ...configuration.polls, - max_characters_per_option: poll_limits.max_option_chars ?? configuration.polls.max_characters_per_option, + max_characters_per_option: + poll_limits.max_option_chars ?? configuration.polls.max_characters_per_option, max_expiration: poll_limits.max_expiration ?? configuration.polls.max_expiration, max_options: poll_limits.max_options ?? configuration.polls.max_options, min_expiration: poll_limits.min_expiration ?? configuration.polls.min_expiration, @@ -67,7 +73,8 @@ const instanceV1ToV2 = (data: any) => { statuses: { ...configuration.statuses, max_characters: max_toot_chars ?? configuration.statuses.max_characters, - max_media_attachments: max_media_attachments ?? configuration.statuses.max_media_attachments, + max_media_attachments: + max_media_attachments ?? configuration.statuses.max_media_attachments, }, urls: { streaming: urls.streaming_api, @@ -97,7 +104,11 @@ const instanceV1ToV2 = (data: any) => { }; }; -const software: Array<[string, string]> = [['takahe', 'Takahe'], ['neodb', 'NeoDB'], ['egregoros', 'Egregoros']]; +const software: Array<[string, string]> = [ + ['takahe', 'Takahe'], + ['neodb', 'NeoDB'], + ['egregoros', 'Egregoros'], +]; const fixVersion = (version: string) => { // Handle Mastodon release candidates @@ -128,11 +139,16 @@ const fixVersion = (version: string) => { }; const configurationSchema = coerceObject({ - accounts: v.fallback(v.nullable(v.object({ - allow_custom_css: v.boolean(), - max_featured_tags: v.pipe(v.number(), v.integer()), - max_profile_fields: v.pipe(v.number(), v.integer()), - })), null), + accounts: v.fallback( + v.nullable( + v.object({ + allow_custom_css: v.boolean(), + max_featured_tags: v.pipe(v.number(), v.integer()), + max_profile_fields: v.pipe(v.number(), v.integer()), + }), + ), + null, + ), chats: coerceObject({ max_characters: v.fallback(v.number(), 5000), }), @@ -162,7 +178,6 @@ const configurationSchema = coerceObject({ characters_reserved_per_url: v.fallback(v.optional(v.number()), undefined), max_characters: v.fallback(v.number(), 500), max_media_attachments: v.fallback(v.number(), 4), - }), translation: coerceObject({ enabled: v.fallback(v.boolean(), false), @@ -211,36 +226,40 @@ const pleromaSchema = coerceObject({ federation: coerceObject({ enabled: v.fallback(v.boolean(), true), // Assume true unless explicitly false mrf_policies: v.fallback(v.optional(v.array(v.string())), undefined), - mrf_simple: coerceObject(v.entriesFromList( - [ - 'accept', - 'avatar_removal', - 'banner_removal', - 'federated_timeline_removal', - 'followers_only', - 'media_nsfw', - 'media_removal', - 'reject', - 'reject_deletes', - 'report_removal', - ], - v.fallback(v.array(v.string()), []), - )), - mrf_simple_info: coerceObject(v.entriesFromList( - [ - 'accept', - 'avatar_removal', - 'banner_removal', - 'federated_timeline_removal', - 'followers_only', - 'media_nsfw', - 'media_removal', - 'reject', - 'reject_deletes', - 'report_removal', - ], - v.fallback(v.array(v.tuple([v.string(), v.string()])), []), - )), + mrf_simple: coerceObject( + v.entriesFromList( + [ + 'accept', + 'avatar_removal', + 'banner_removal', + 'federated_timeline_removal', + 'followers_only', + 'media_nsfw', + 'media_removal', + 'reject', + 'reject_deletes', + 'report_removal', + ], + v.fallback(v.array(v.string()), []), + ), + ), + mrf_simple_info: coerceObject( + v.entriesFromList( + [ + 'accept', + 'avatar_removal', + 'banner_removal', + 'federated_timeline_removal', + 'followers_only', + 'media_nsfw', + 'media_removal', + 'reject', + 'reject_deletes', + 'report_removal', + ], + v.fallback(v.array(v.tuple([v.string(), v.string()])), []), + ), + ), }), fields_limits: coerceObject({ max_fields: v.fallback(v.pipe(v.number(), v.integer(), v.minValue(0)), 4), @@ -253,13 +272,15 @@ const pleromaSchema = coerceObject({ }), migration_cooldown_period: v.fallback(v.optional(v.number()), undefined), multitenancy: coerceObject({ - domains: v.optional(v.array( - v.object({ - domain: v.pipe(v.unknown(), v.transform(String)), - id: v.string(), - public: v.fallback(v.boolean(), false), - }), - )), + domains: v.optional( + v.array( + v.object({ + domain: v.pipe(v.unknown(), v.transform(String)), + id: v.string(), + public: v.fallback(v.boolean(), false), + }), + ), + ), enabled: v.fallback(v.boolean(), false), }), post_formats: v.fallback(v.array(v.string()), ['text/plain']), @@ -376,7 +397,11 @@ const instanceSchema = v.pipe( if (!data.pleroma) { data.pleroma = { metadata: { - post_formats: data.configuration?.statuses?.supported_mime_types || (apiVersions['kmyblue_markdown.fedibird.pl-api'] ? ['text/plain', 'text/markdown'] : []), + post_formats: + data.configuration?.statuses?.supported_mime_types || + (apiVersions['kmyblue_markdown.fedibird.pl-api'] + ? ['text/plain', 'text/markdown'] + : []), }, }; } @@ -390,10 +415,18 @@ const instanceSchema = v.pipe( data.configuration.timelines_access = { ...data.configuration.timelines_access, live_feeds: { - bubble: timelines.bubble ? 'authenticated' : features.includes('bubble_timeline') ? 'public' : 'disabled', + bubble: timelines.bubble + ? 'authenticated' + : features.includes('bubble_timeline') + ? 'public' + : 'disabled', local: timelines.local ? 'authenticated' : 'public', remote: timelines.federated ? 'authenticated' : 'public', - wrenched: timelines.wrenched ? 'authenticated' : features.includes('pleroma:wrenched_timeline') ? 'public' : 'disabled', + wrenched: timelines.wrenched + ? 'authenticated' + : features.includes('pleroma:wrenched_timeline') + ? 'public' + : 'disabled', }, }; } @@ -410,10 +443,12 @@ const instanceSchema = v.pipe( description: v.fallback(v.string(), ''), domain: v.fallback(v.string(), ''), feature_quote: v.fallback(v.boolean(), false), - icons: filteredArray(v.object({ - size: v.pipe(v.string(), v.regex(/^[0-9]+x[0-9]+$/)), - src: v.string(), - })), + icons: filteredArray( + v.object({ + size: v.pipe(v.string(), v.regex(/^[0-9]+x[0-9]+$/)), + src: v.string(), + }), + ), languages: v.fallback(v.array(v.string()), []), pleroma: pleromaSchema, registrations, @@ -423,17 +458,24 @@ const instanceSchema = v.pipe( title: v.fallback(v.string(), ''), usage: usageSchema, version: v.pipe(v.fallback(v.string(), '0.0.0'), v.transform(fixVersion)), - blockchains: v.fallback(v.optional(filteredArray(v.object({ - chain_id: v.fallback(v.nullable(v.string()), null), - chain_metadata: coerceObject({ - is_forwarding_required: v.fallback(v.boolean(), false), - description: v.fallback(v.string(), ''), - payment_amount_min: v.number(), - }), - features: coerceObject({ - subscriptions: v.fallback(v.boolean(), false), - }), - }))), undefined), + blockchains: v.fallback( + v.optional( + filteredArray( + v.object({ + chain_id: v.fallback(v.nullable(v.string()), null), + chain_metadata: coerceObject({ + is_forwarding_required: v.fallback(v.boolean(), false), + description: v.fallback(v.string(), ''), + payment_amount_min: v.number(), + }), + features: coerceObject({ + subscriptions: v.fallback(v.boolean(), false), + }), + }), + ), + ), + undefined, + ), }), ); diff --git a/packages/pl-api/lib/entities/interaction-policy.ts b/packages/pl-api/lib/entities/interaction-policy.ts index 36df12137..78f17de70 100644 --- a/packages/pl-api/lib/entities/interaction-policy.ts +++ b/packages/pl-api/lib/entities/interaction-policy.ts @@ -2,7 +2,15 @@ import * as v from 'valibot'; import { coerceObject } from './utils'; -const interactionPolicyEntrySchema = v.picklist(['public', 'followers', 'following', 'mutuals', 'mentioned', 'author', 'me']); +const interactionPolicyEntrySchema = v.picklist([ + 'public', + 'followers', + 'following', + 'mutuals', + 'mentioned', + 'author', + 'me', +]); /** * @category Entity types @@ -44,5 +52,10 @@ const interactionPoliciesSchema = coerceObject({ */ type InteractionPolicies = v.InferOutput; -export { interactionPolicySchema, interactionPoliciesSchema, type InteractionPolicyEntry, type InteractionPolicy, type InteractionPolicies }; - +export { + interactionPolicySchema, + interactionPoliciesSchema, + type InteractionPolicyEntry, + type InteractionPolicy, + type InteractionPolicies, +}; diff --git a/packages/pl-api/lib/entities/item.ts b/packages/pl-api/lib/entities/item.ts index fc1c80930..273a18f5b 100644 --- a/packages/pl-api/lib/entities/item.ts +++ b/packages/pl-api/lib/entities/item.ts @@ -11,20 +11,33 @@ const baseItemSchema = v.object({ uuid: v.string(), url: v.string(), api_url: v.string(), - category: v.picklist(['book', 'movie', 'tv', 'music', 'game', 'podcast', 'performance', 'collection']), + category: v.picklist([ + 'book', + 'movie', + 'tv', + 'music', + 'game', + 'podcast', + 'performance', + 'collection', + ]), parent_uuid: v.nullable(v.string()), display_title: v.string(), external_resources: v.nullable(filteredArray(externalResourceSchema)), title: v.string(), description: v.string(), - localized_title: filteredArray(v.object({ - lang: v.string(), - text: v.string(), - })), - localized_description: filteredArray(v.object({ - lang: v.string(), - text: v.string(), - })), + localized_title: filteredArray( + v.object({ + lang: v.string(), + text: v.string(), + }), + ), + localized_description: filteredArray( + v.object({ + lang: v.string(), + text: v.string(), + }), + ), conver_image_url: v.nullable(v.string()), rating: v.nullable(v.number()), rating_count: v.nullable(v.number()), @@ -214,17 +227,17 @@ const itemSchema: v.BaseSchema> = v.pipe( * @category Entity types */ type Item = v.InferOutput< -| typeof editionSchema -| typeof tvShowSchema -| typeof tvSeasonSchema -| typeof movieSchema -| typeof albumSchema -| typeof podcastSchema -| typeof gameSchema -| typeof performanceSchema -| typeof podcastEpisodeSchema -| typeof performanceProductionSchema -| typeof tvEpisodeSchema + | typeof editionSchema + | typeof tvShowSchema + | typeof tvSeasonSchema + | typeof movieSchema + | typeof albumSchema + | typeof podcastSchema + | typeof gameSchema + | typeof performanceSchema + | typeof podcastEpisodeSchema + | typeof performanceProductionSchema + | typeof tvEpisodeSchema >; export { diff --git a/packages/pl-api/lib/entities/location.ts b/packages/pl-api/lib/entities/location.ts index 731e95ec9..6023038e7 100644 --- a/packages/pl-api/lib/entities/location.ts +++ b/packages/pl-api/lib/entities/location.ts @@ -15,10 +15,15 @@ const locationSchema = v.object({ origin_provider: v.fallback(v.string(), ''), type: v.fallback(v.string(), ''), timezone: v.fallback(v.string(), ''), - geom: v.fallback(v.nullable(v.object({ - coordinates: v.fallback(v.nullable(v.tuple([v.number(), v.number()])), null), - srid: v.fallback(v.string(), ''), - })), null), + geom: v.fallback( + v.nullable( + v.object({ + coordinates: v.fallback(v.nullable(v.tuple([v.number(), v.number()])), null), + srid: v.fallback(v.string(), ''), + }), + ), + null, + ), }); /** diff --git a/packages/pl-api/lib/entities/marker.ts b/packages/pl-api/lib/entities/marker.ts index a0b370f21..f71e5a2e9 100644 --- a/packages/pl-api/lib/entities/marker.ts +++ b/packages/pl-api/lib/entities/marker.ts @@ -8,10 +8,14 @@ import { datetimeSchema } from './utils'; */ const markerSchema = v.pipe( v.any(), - v.transform((marker: any) => marker ? ({ - unread_count: marker.pleroma?.unread_count, - ...marker, - }) : null), + v.transform((marker: any) => + marker + ? { + unread_count: marker.pleroma?.unread_count, + ...marker, + } + : null, + ), v.object({ last_read_id: v.string(), version: v.pipe(v.number(), v.integer()), @@ -35,9 +39,4 @@ const markersSchema = v.record(v.string(), markerSchema); */ type Markers = v.InferOutput; -export { - markerSchema, - markersSchema, - type Marker, - type Markers, -}; +export { markerSchema, markersSchema, type Marker, type Markers }; diff --git a/packages/pl-api/lib/entities/media-attachment.ts b/packages/pl-api/lib/entities/media-attachment.ts index 187909889..8f9ff760d 100644 --- a/packages/pl-api/lib/entities/media-attachment.ts +++ b/packages/pl-api/lib/entities/media-attachment.ts @@ -6,10 +6,10 @@ import { mimeSchema } from './utils'; /** * @category Schemas */ -const blurhashSchema = v.pipe(v.string(), v.check( - (value) => isBlurhashValid(value).result, - 'invalid blurhash', -)); +const blurhashSchema = v.pipe( + v.string(), + v.check((value) => isBlurhashValid(value).result, 'invalid blurhash'), +); const baseAttachmentSchema = v.object({ id: v.string(), @@ -33,60 +33,97 @@ const imageMetaSchema = v.object({ const imageAttachmentSchema = v.object({ ...baseAttachmentSchema.entries, type: v.literal('image'), - meta: v.fallback(v.object({ - original: v.fallback(v.optional(imageMetaSchema), undefined), - small: v.fallback(v.optional(imageMetaSchema), undefined), - focus: v.fallback(v.optional(v.object({ - x: v.pipe(v.number(), v.minValue(-1), v.maxValue(1)), - y: v.pipe(v.number(), v.minValue(-1), v.maxValue(1)), - })), undefined), - }), {}), + meta: v.fallback( + v.object({ + original: v.fallback(v.optional(imageMetaSchema), undefined), + small: v.fallback(v.optional(imageMetaSchema), undefined), + focus: v.fallback( + v.optional( + v.object({ + x: v.pipe(v.number(), v.minValue(-1), v.maxValue(1)), + y: v.pipe(v.number(), v.minValue(-1), v.maxValue(1)), + }), + ), + undefined, + ), + }), + {}, + ), }); const videoAttachmentSchema = v.object({ ...baseAttachmentSchema.entries, type: v.literal('video'), - meta: v.fallback(v.object({ - duration: v.fallback(v.optional(v.number()), undefined), - original: v.fallback(v.optional(v.object({ - ...imageMetaSchema.entries, - frame_rate: v.fallback(v.nullable(v.pipe(v.string(), v.regex(/\d+\/\d+$/))), null), - duration: v.fallback(v.nullable(v.pipe(v.number(), v.minValue(0))), null), - })), undefined), - small: v.fallback(v.optional(imageMetaSchema), undefined), - // WIP: add rest - }), {}), + meta: v.fallback( + v.object({ + duration: v.fallback(v.optional(v.number()), undefined), + original: v.fallback( + v.optional( + v.object({ + ...imageMetaSchema.entries, + frame_rate: v.fallback(v.nullable(v.pipe(v.string(), v.regex(/\d+\/\d+$/))), null), + duration: v.fallback(v.nullable(v.pipe(v.number(), v.minValue(0))), null), + }), + ), + undefined, + ), + small: v.fallback(v.optional(imageMetaSchema), undefined), + // WIP: add rest + }), + {}, + ), }); const gifvAttachmentSchema = v.object({ ...baseAttachmentSchema.entries, type: v.literal('gifv'), - meta: v.fallback(v.object({ - duration: v.fallback(v.optional(v.number()), undefined), - original: v.fallback(v.optional(imageMetaSchema), undefined), - focus: v.fallback(v.optional(v.object({ - x: v.pipe(v.number(), v.minValue(-1), v.maxValue(1)), - y: v.pipe(v.number(), v.minValue(-1), v.maxValue(1)), - })), undefined), - }), {}), + meta: v.fallback( + v.object({ + duration: v.fallback(v.optional(v.number()), undefined), + original: v.fallback(v.optional(imageMetaSchema), undefined), + focus: v.fallback( + v.optional( + v.object({ + x: v.pipe(v.number(), v.minValue(-1), v.maxValue(1)), + y: v.pipe(v.number(), v.minValue(-1), v.maxValue(1)), + }), + ), + undefined, + ), + }), + {}, + ), }); const audioAttachmentSchema = v.object({ ...baseAttachmentSchema.entries, type: v.literal('audio'), - meta: v.fallback(v.object({ - duration: v.fallback(v.optional(v.number()), undefined), - colors: v.fallback(v.optional(v.object({ - background: v.fallback(v.optional(v.string()), undefined), - foreground: v.fallback(v.optional(v.string()), undefined), - accent: v.fallback(v.optional(v.string()), undefined), + meta: v.fallback( + v.object({ duration: v.fallback(v.optional(v.number()), undefined), - })), undefined), - original: v.fallback(v.optional(v.object({ - duration: v.fallback(v.optional(v.number()), undefined), - bitrate: v.fallback(v.optional(v.pipe(v.number(), v.minValue(0))), undefined), - })), undefined), - }), {}), + colors: v.fallback( + v.optional( + v.object({ + background: v.fallback(v.optional(v.string()), undefined), + foreground: v.fallback(v.optional(v.string()), undefined), + accent: v.fallback(v.optional(v.string()), undefined), + duration: v.fallback(v.optional(v.number()), undefined), + }), + ), + undefined, + ), + original: v.fallback( + v.optional( + v.object({ + duration: v.fallback(v.optional(v.number()), undefined), + bitrate: v.fallback(v.optional(v.pipe(v.number(), v.minValue(0))), undefined), + }), + ), + undefined, + ), + }), + {}, + ), }); const unknownAttachmentSchema = v.object({ diff --git a/packages/pl-api/lib/entities/notification.ts b/packages/pl-api/lib/entities/notification.ts index 138c6a3a4..e260045fe 100644 --- a/packages/pl-api/lib/entities/notification.ts +++ b/packages/pl-api/lib/entities/notification.ts @@ -34,7 +34,16 @@ const mentionNotificationSchema = v.object({ const statusNotificationSchema = v.object({ ...baseNotificationSchema.entries, - type: v.picklist(['status', 'reblog', 'favourite', 'poll', 'update', 'quote', 'quoted_update', 'event_reminder']), + type: v.picklist([ + 'status', + 'reblog', + 'favourite', + 'poll', + 'update', + 'quote', + 'quoted_update', + 'event_reminder', + ]), status: statusSchema, }); @@ -99,11 +108,12 @@ const notificationSchema: v.BaseSchema> group_key: `ungrouped-${notification.id}`, ...pick(notification.pleroma || {}, ['is_muted', 'is_seen']), ...notification, - type: notification.type === 'pleroma:report' - ? 'admin.report' - : notification.type === 'reaction' - ? 'emoji_reaction' - : notification.type?.replace(/^pleroma:/, ''), + type: + notification.type === 'pleroma:report' + ? 'admin.report' + : notification.type === 'reaction' + ? 'emoji_reaction' + : notification.type?.replace(/^pleroma:/, ''), })), v.variant('type', [ accountNotificationSchema, @@ -117,23 +127,24 @@ const notificationSchema: v.BaseSchema> chatMentionNotificationSchema, eventParticipationRequestNotificationSchema, biteNotificationSchema, - ])) as any; + ]), +) as any; /** * @category Entity types */ type Notification = v.InferOutput< -| typeof accountNotificationSchema -| typeof mentionNotificationSchema -| typeof statusNotificationSchema -| typeof reportNotificationSchema -| typeof severedRelationshipNotificationSchema -| typeof moderationWarningNotificationSchema -| typeof moveNotificationSchema -| typeof emojiReactionNotificationSchema -| typeof chatMentionNotificationSchema -| typeof eventParticipationRequestNotificationSchema -| typeof biteNotificationSchema + | typeof accountNotificationSchema + | typeof mentionNotificationSchema + | typeof statusNotificationSchema + | typeof reportNotificationSchema + | typeof severedRelationshipNotificationSchema + | typeof moderationWarningNotificationSchema + | typeof moveNotificationSchema + | typeof emojiReactionNotificationSchema + | typeof chatMentionNotificationSchema + | typeof eventParticipationRequestNotificationSchema + | typeof biteNotificationSchema >; export { notificationSchema, type Notification }; diff --git a/packages/pl-api/lib/entities/quote.ts b/packages/pl-api/lib/entities/quote.ts index ae246a352..70efe36bb 100644 --- a/packages/pl-api/lib/entities/quote.ts +++ b/packages/pl-api/lib/entities/quote.ts @@ -2,7 +2,17 @@ import * as v from 'valibot'; import { statusSchema } from './status'; -const quoteStateSchema = v.picklist(['pending', 'accepted', 'rejected', 'revoked', 'deleted', 'unauthorized', 'blocked_account', 'blocked_domain', 'muted-account']); +const quoteStateSchema = v.picklist([ + 'pending', + 'accepted', + 'rejected', + 'revoked', + 'deleted', + 'unauthorized', + 'blocked_account', + 'blocked_domain', + 'muted-account', +]); /** * @category Schemas diff --git a/packages/pl-api/lib/entities/review.ts b/packages/pl-api/lib/entities/review.ts index 3b498607e..f07ee8236 100644 --- a/packages/pl-api/lib/entities/review.ts +++ b/packages/pl-api/lib/entities/review.ts @@ -24,7 +24,4 @@ const reviewSchema = v.object({ */ type Review = v.InferOutput; -export { - reviewSchema, - type Review, -}; +export { reviewSchema, type Review }; diff --git a/packages/pl-api/lib/entities/role.ts b/packages/pl-api/lib/entities/role.ts index 14584738e..a26f356e7 100644 --- a/packages/pl-api/lib/entities/role.ts +++ b/packages/pl-api/lib/entities/role.ts @@ -18,7 +18,4 @@ const roleSchema = v.object({ */ type Role = v.InferOutput; -export { - roleSchema, - type Role, -}; +export { roleSchema, type Role }; diff --git a/packages/pl-api/lib/entities/rule.ts b/packages/pl-api/lib/entities/rule.ts index aea0fd4f0..410fbac00 100644 --- a/packages/pl-api/lib/entities/rule.ts +++ b/packages/pl-api/lib/entities/rule.ts @@ -4,10 +4,16 @@ const baseRuleSchema = v.object({ id: v.string(), text: v.fallback(v.string(), ''), hint: v.fallback(v.string(), ''), - translations: v.optional(v.record(v.string(), v.object({ - text: v.fallback(v.string(), ''), - hint: v.fallback(v.string(), ''), - })), undefined), + translations: v.optional( + v.record( + v.string(), + v.object({ + text: v.fallback(v.string(), ''), + hint: v.fallback(v.string(), ''), + }), + ), + undefined, + ), }); /** diff --git a/packages/pl-api/lib/entities/scheduled-status.ts b/packages/pl-api/lib/entities/scheduled-status.ts index 17bb70716..be136785c 100644 --- a/packages/pl-api/lib/entities/scheduled-status.ts +++ b/packages/pl-api/lib/entities/scheduled-status.ts @@ -12,12 +12,17 @@ const scheduledStatusSchema = v.object({ scheduled_at: datetimeSchema, params: v.object({ text: v.fallback(v.nullable(v.string()), null), - poll: v.fallback(v.nullable(v.object({ - options: v.array(v.string()), - expires_in: v.pipe(v.unknown(), v.transform(String)), - multiple: v.fallback(v.optional(v.boolean()), undefined), - hide_totals: v.fallback(v.optional(v.boolean()), undefined), - })), null), + poll: v.fallback( + v.nullable( + v.object({ + options: v.array(v.string()), + expires_in: v.pipe(v.unknown(), v.transform(String)), + multiple: v.fallback(v.optional(v.boolean()), undefined), + hide_totals: v.fallback(v.optional(v.boolean()), undefined), + }), + ), + null, + ), media_ids: v.fallback(v.nullable(v.string()), null), sensitive: v.fallback(v.nullable(v.pipe(v.unknown(), v.transform(Boolean))), null), spoiler_text: v.fallback(v.nullable(v.string()), null), diff --git a/packages/pl-api/lib/entities/scrobble.ts b/packages/pl-api/lib/entities/scrobble.ts index b1b2d50ac..271077eb6 100644 --- a/packages/pl-api/lib/entities/scrobble.ts +++ b/packages/pl-api/lib/entities/scrobble.ts @@ -8,10 +8,14 @@ import { datetimeSchema } from './utils'; */ const scrobbleSchema = v.pipe( v.any(), - v.transform((scrobble: any) => scrobble ? { - external_link: scrobble.externalLink, - ...scrobble, - } : null), + v.transform((scrobble: any) => + scrobble + ? { + external_link: scrobble.externalLink, + ...scrobble, + } + : null, + ), v.object({ id: v.pipe(v.unknown(), v.transform(String)), account: accountSchema, diff --git a/packages/pl-api/lib/entities/status-edit.ts b/packages/pl-api/lib/entities/status-edit.ts index 46a3c159c..f45627a2b 100644 --- a/packages/pl-api/lib/entities/status-edit.ts +++ b/packages/pl-api/lib/entities/status-edit.ts @@ -15,11 +15,18 @@ const statusEditSchema = v.object({ sensitive: v.pipe(v.unknown(), v.transform(Boolean)), created_at: v.fallback(datetimeSchema, new Date().toISOString()), account: accountSchema, - poll: v.fallback(v.nullable(v.object({ - options: v.array(v.object({ - title: v.string(), - })), - })), null), + poll: v.fallback( + v.nullable( + v.object({ + options: v.array( + v.object({ + title: v.string(), + }), + ), + }), + ), + null, + ), media_attachments: filteredArray(mediaAttachmentSchema), emojis: filteredArray(customEmojiSchema), }); diff --git a/packages/pl-api/lib/entities/status.ts b/packages/pl-api/lib/entities/status.ts index 859624fc6..3547fb52a 100644 --- a/packages/pl-api/lib/entities/status.ts +++ b/packages/pl-api/lib/entities/status.ts @@ -46,19 +46,35 @@ const baseStatusSchema = v.object({ id: v.string(), uri: v.fallback(v.pipe(v.string(), v.url()), ''), created_at: v.fallback(datetimeSchema, new Date().toISOString()), - account: v.pipe(v.unknown(), v.transform((account) => { - if ((window as any).__PL_API_FALLBACK_ACCOUNT && JSON.stringify(account) === '{}') return (window as any).__PL_API_FALLBACK_ACCOUNT; - return account; - }), accountSchema), - content: v.fallback(v.pipe(v.string(), v.transform((note => note === '

' ? '' : note))), ''), + account: v.pipe( + v.unknown(), + v.transform((account) => { + if ((window as any).__PL_API_FALLBACK_ACCOUNT && JSON.stringify(account) === '{}') + return (window as any).__PL_API_FALLBACK_ACCOUNT; + return account; + }), + accountSchema, + ), + content: v.fallback( + v.pipe( + v.string(), + v.transform((note) => (note === '

' ? '' : note)), + ), + '', + ), visibility: v.fallback(v.string(), 'public'), sensitive: v.pipe(v.unknown(), v.transform(Boolean)), spoiler_text: v.fallback(v.string(), ''), media_attachments: filteredArray(mediaAttachmentSchema), - application: v.fallback(v.nullable(v.object({ - name: v.string(), - website: v.fallback(v.nullable(v.pipe(v.string(), v.url())), null), - })), null), + application: v.fallback( + v.nullable( + v.object({ + name: v.string(), + website: v.fallback(v.nullable(v.pipe(v.string(), v.url())), null), + }), + ), + null, + ), mentions: filteredArray(mentionSchema), tags: filteredArray(tagSchema), emojis: filteredArray(customEmojiSchema), @@ -121,13 +137,16 @@ const baseStatusSchema = v.object({ const preprocess = (status: any) => { if (!status) return null; - let quote: { - state: string; - quoted_status: any; - } | { - state: string; - quoted_status_id: string; - } | null = null; + let quote: + | { + state: string; + quoted_status: any; + } + | { + state: string; + quoted_status_id: string; + } + | null = null; const quotedStatus = status.quote ?? status.pleroma?.quote; let quotedStatusId = quotedStatus?.id ?? status.quote_id ?? status.pleroma?.quote_id; @@ -149,9 +168,9 @@ const preprocess = (status: any) => { } status = { - // @ts-ignore + // @ts-expect-error only overrides if present in pleroma object emoji_reactions: status.reactions, - ...(pick(status.pleroma || {}, [ + ...pick(status.pleroma || {}, [ 'local', 'conversation_id', 'direct_conversation_id', @@ -171,11 +190,8 @@ const preprocess = (status: any) => { 'translation', 'rss_feed', 'location', - ])), - ...(pick(status.friendica || {}, [ - 'dislikes_count', - 'disliked', - ])), + ]), + ...pick(status.friendica || {}, ['dislikes_count', 'disliked']), ...status, quote, quote_id: quotedStatusId, @@ -195,29 +211,39 @@ const preprocess = (status: any) => { /** * @category Schemas */ -const statusSchema: v.BaseSchema> = v.pipe(v.any(), v.transform(preprocess), v.object({ - ...baseStatusSchema.entries, - reblog: v.fallback(v.nullable(v.lazy(() => statusSchema)), null), +const statusSchema: v.BaseSchema> = v.pipe( + v.any(), + v.transform(preprocess), + v.object({ + ...baseStatusSchema.entries, + reblog: v.fallback(v.nullable(v.lazy(() => statusSchema)), null), - quote: v.fallback(v.nullable(v.lazy(() => v.union([quoteSchema, shallowQuoteSchema]))), null), -})) as any; + quote: v.fallback(v.nullable(v.lazy(() => v.union([quoteSchema, shallowQuoteSchema]))), null), + }), +) as any; /** * @category Schemas */ -const statusWithoutAccountSchema = v.pipe(v.any(), v.transform(preprocess), v.object({ - ...(v.omit(baseStatusSchema, ['account']).entries), - account: v.fallback(v.nullable(accountSchema), null), - reblog: v.fallback(v.nullable(v.lazy(() => statusSchema)), null), +const statusWithoutAccountSchema = v.pipe( + v.any(), + v.transform(preprocess), + v.object({ + ...v.omit(baseStatusSchema, ['account']).entries, + account: v.fallback(v.nullable(accountSchema), null), + reblog: v.fallback(v.nullable(v.lazy(() => statusSchema)), null), - quote: v.fallback(v.nullable(v.lazy(() => v.union([quoteSchema, shallowQuoteSchema]))), null), -})); + quote: v.fallback(v.nullable(v.lazy(() => v.union([quoteSchema, shallowQuoteSchema]))), null), + }), +); -const partialStatusSchema = v.partial(v.object({ - ...baseStatusSchema.entries, - reblog: v.fallback(v.nullable(v.lazy(() => statusSchema)), null), - quote: v.fallback(v.nullable(v.lazy(() => v.union([quoteSchema, shallowQuoteSchema]))), null), -})); +const partialStatusSchema = v.partial( + v.object({ + ...baseStatusSchema.entries, + reblog: v.fallback(v.nullable(v.lazy(() => statusSchema)), null), + quote: v.fallback(v.nullable(v.lazy(() => v.union([quoteSchema, shallowQuoteSchema]))), null), + }), +); /** * @category Entity types @@ -226,7 +252,7 @@ type StatusWithoutAccount = Omit, 'accoun account: Account | null; reblog: Status | null; quote: Quote | ShallowQuote | null; -} +}; /** * @category Entity types @@ -234,6 +260,12 @@ type StatusWithoutAccount = Omit, 'accoun type Status = v.InferOutput & { reblog: Status | null; quote: Quote | ShallowQuote | null; -} +}; -export { statusSchema, statusWithoutAccountSchema, partialStatusSchema, type Status, type StatusWithoutAccount }; +export { + statusSchema, + statusWithoutAccountSchema, + partialStatusSchema, + type Status, + type StatusWithoutAccount, +}; diff --git a/packages/pl-api/lib/entities/story-carousel-item.ts b/packages/pl-api/lib/entities/story-carousel-item.ts index 241b48698..7bb61a04d 100644 --- a/packages/pl-api/lib/entities/story-carousel-item.ts +++ b/packages/pl-api/lib/entities/story-carousel-item.ts @@ -3,24 +3,28 @@ import * as v from 'valibot'; /** * @category Schemas */ -const storyCarouselItemSchema = v.pipe(v.any(), v.transform((item) => ({ - account_id: item.pid, - story_id: item.sid, - ...item, -})), v.object({ - account_id: v.string(), - avatar: v.string(), - local: v.boolean(), - username: v.string(), - latest: v.object({ - id: v.pipe(v.unknown(), v.transform(String)), - type: v.string(), - preview_url: v.string(), +const storyCarouselItemSchema = v.pipe( + v.any(), + v.transform((item) => ({ + account_id: item.pid, + story_id: item.sid, + ...item, + })), + v.object({ + account_id: v.string(), + avatar: v.string(), + local: v.boolean(), + username: v.string(), + latest: v.object({ + id: v.pipe(v.unknown(), v.transform(String)), + type: v.string(), + preview_url: v.string(), + }), + url: v.string(), + seen: v.boolean(), + story_id: v.pipe(v.unknown(), v.transform(String)), }), - url: v.string(), - seen: v.boolean(), - story_id: v.pipe(v.unknown(), v.transform(String)), -})); +); /** * @category Entity types diff --git a/packages/pl-api/lib/entities/story-media.ts b/packages/pl-api/lib/entities/story-media.ts index 0f42fa4b0..f3e7aaee0 100644 --- a/packages/pl-api/lib/entities/story-media.ts +++ b/packages/pl-api/lib/entities/story-media.ts @@ -3,15 +3,19 @@ import * as v from 'valibot'; /** * @category Schemas */ -const storyMediaSchema = v.pipe(v.any(), v.transform((media) => ({ - id: media.media_id, - url: media.media_url, - type: media.media_type, -})), v.object({ - id: v.string(), - url: v.string(), - type: v.picklist(['photo', 'video']), -})); +const storyMediaSchema = v.pipe( + v.any(), + v.transform((media) => ({ + id: media.media_id, + url: media.media_url, + type: media.media_type, + })), + v.object({ + id: v.string(), + url: v.string(), + type: v.picklist(['photo', 'video']), + }), +); /** * @category Entity types diff --git a/packages/pl-api/lib/entities/streaming-event.ts b/packages/pl-api/lib/entities/streaming-event.ts index c971d02c9..ec2eef477 100644 --- a/packages/pl-api/lib/entities/streaming-event.ts +++ b/packages/pl-api/lib/entities/streaming-event.ts @@ -38,7 +38,11 @@ const baseStreamingEventSchema = v.object({ const statusStreamingEventSchema = v.object({ ...baseStreamingEventSchema.entries, event: v.picklist(['update', 'status.update']), - payload: v.pipe(v.any(), v.transform((payload: any) => JSON.parse(payload)), statusSchema), + payload: v.pipe( + v.any(), + v.transform((payload: any) => JSON.parse(payload)), + statusSchema, + ), }); const stringStreamingEventSchema = v.object({ @@ -50,7 +54,11 @@ const stringStreamingEventSchema = v.object({ const notificationStreamingEventSchema = v.object({ ...baseStreamingEventSchema.entries, event: v.literal('notification'), - payload: v.pipe(v.any(), v.transform((payload: any) => JSON.parse(payload)), notificationSchema), + payload: v.pipe( + v.any(), + v.transform((payload: any) => JSON.parse(payload)), + notificationSchema, + ), }); const emptyStreamingEventSchema = v.object({ @@ -61,46 +69,74 @@ const emptyStreamingEventSchema = v.object({ const conversationStreamingEventSchema = v.object({ ...baseStreamingEventSchema.entries, event: v.literal('conversation'), - payload: v.pipe(v.any(), v.transform((payload: any) => JSON.parse(payload)), conversationSchema), + payload: v.pipe( + v.any(), + v.transform((payload: any) => JSON.parse(payload)), + conversationSchema, + ), }); const announcementStreamingEventSchema = v.object({ ...baseStreamingEventSchema.entries, event: v.literal('announcement'), - payload: v.pipe(v.any(), v.transform((payload: any) => JSON.parse(payload)), announcementSchema), + payload: v.pipe( + v.any(), + v.transform((payload: any) => JSON.parse(payload)), + announcementSchema, + ), }); const announcementReactionStreamingEventSchema = v.object({ ...baseStreamingEventSchema.entries, event: v.literal('announcement.reaction'), - payload: v.pipe(v.any(), v.transform((payload: any) => JSON.parse(payload)), announcementReactionSchema), + payload: v.pipe( + v.any(), + v.transform((payload: any) => JSON.parse(payload)), + announcementReactionSchema, + ), }); const chatUpdateStreamingEventSchema = v.object({ ...baseStreamingEventSchema.entries, event: v.literal('chat_update'), - payload: v.pipe(v.any(), v.transform((payload: any) => JSON.parse(payload)), chatSchema), + payload: v.pipe( + v.any(), + v.transform((payload: any) => JSON.parse(payload)), + chatSchema, + ), }); const followRelationshipsUpdateStreamingEventSchema = v.object({ ...baseStreamingEventSchema.entries, event: v.literal('follow_relationships_update'), - payload: v.pipe(v.any(), v.transform((payload: any) => JSON.parse(payload)), followRelationshipUpdateSchema), + payload: v.pipe( + v.any(), + v.transform((payload: any) => JSON.parse(payload)), + followRelationshipUpdateSchema, + ), }); const respondStreamingEventSchema = v.object({ ...baseStreamingEventSchema.entries, event: v.literal('respond'), - payload: v.pipe(v.any(), v.transform((payload: any) => JSON.parse(payload)), v.object({ - type: v.string(), - result: v.picklist(['success', 'ignored', 'error']), - })), + payload: v.pipe( + v.any(), + v.transform((payload: any) => JSON.parse(payload)), + v.object({ + type: v.string(), + result: v.picklist(['success', 'ignored', 'error']), + }), + ), }); const markerStreamingEventSchema = v.object({ ...baseStreamingEventSchema.entries, event: v.literal('marker'), - payload: v.pipe(v.any(), v.transform((payload: any) => JSON.parse(payload)), markersSchema), + payload: v.pipe( + v.any(), + v.transform((payload: any) => JSON.parse(payload)), + markersSchema, + ), }); const notificationsMergedEventSchema = v.object({ @@ -111,7 +147,11 @@ const notificationsMergedEventSchema = v.object({ const emojiReactionStreamingEventSchema = v.object({ ...baseStreamingEventSchema.entries, event: v.literal('emoji_reaction'), - payload: v.pipe(v.any(), v.transform((payload: any) => JSON.parse(payload)), emojiReactionSchema), + payload: v.pipe( + v.any(), + v.transform((payload: any) => JSON.parse(payload)), + emojiReactionSchema, + ), }); /** @@ -145,18 +185,18 @@ const streamingEventSchema: v.BaseSchema; export { diff --git a/packages/pl-api/lib/entities/subscription-details.ts b/packages/pl-api/lib/entities/subscription-details.ts index 1bb5fd8a0..29665ec72 100644 --- a/packages/pl-api/lib/entities/subscription-details.ts +++ b/packages/pl-api/lib/entities/subscription-details.ts @@ -17,7 +17,4 @@ const subscriptionDetailsSchema = v.object({ */ type SubscriptionDetails = v.InferOutput; -export { - subscriptionDetailsSchema, - type SubscriptionDetails, -}; +export { subscriptionDetailsSchema, type SubscriptionDetails }; diff --git a/packages/pl-api/lib/entities/subscription-invoice.ts b/packages/pl-api/lib/entities/subscription-invoice.ts index 47aa4e283..2d6f09ae7 100644 --- a/packages/pl-api/lib/entities/subscription-invoice.ts +++ b/packages/pl-api/lib/entities/subscription-invoice.ts @@ -19,7 +19,16 @@ const subscriptionInvoiceSchema = v.object({ /** Requested payment amount (in atomic units). */ amount: v.number(), /** Invoice status. */ - status: v.picklist(['open', 'paid', 'forwarded', 'timeout', 'cancelled', 'underpaid', 'completed', 'failed']), + status: v.picklist([ + 'open', + 'paid', + 'forwarded', + 'timeout', + 'cancelled', + 'underpaid', + 'completed', + 'failed', + ]), /** The date when invoice was created. */ created_at: datetimeSchema, /** The date when invoice times out. */ @@ -31,7 +40,4 @@ const subscriptionInvoiceSchema = v.object({ */ type SubscriptionInvoice = v.InferOutput; -export { - subscriptionInvoiceSchema, - type SubscriptionInvoice, -}; +export { subscriptionInvoiceSchema, type SubscriptionInvoice }; diff --git a/packages/pl-api/lib/entities/subscription-option.ts b/packages/pl-api/lib/entities/subscription-option.ts index 9b1ba02a4..6f4958c7f 100644 --- a/packages/pl-api/lib/entities/subscription-option.ts +++ b/packages/pl-api/lib/entities/subscription-option.ts @@ -21,7 +21,4 @@ const subscriptionOptionSchema = v.variant('type', [ */ type SubscriptionOption = v.InferOutput; -export { - subscriptionOptionSchema, - type SubscriptionOption, -}; +export { subscriptionOptionSchema, type SubscriptionOption }; diff --git a/packages/pl-api/lib/entities/suggestion.ts b/packages/pl-api/lib/entities/suggestion.ts index 897f42cf2..9f61c8cb8 100644 --- a/packages/pl-api/lib/entities/suggestion.ts +++ b/packages/pl-api/lib/entities/suggestion.ts @@ -9,17 +9,18 @@ import { accountSchema } from './account'; const suggestionSchema = v.pipe( v.any(), v.transform((suggestion: any) => { - /** - * Support `/api/v1/suggestions` - * @see {@link https://docs.joinmastodon.org/methods/suggestions/#v1} - */ + /** + * Support `/api/v1/suggestions` + * @see {@link https://docs.joinmastodon.org/methods/suggestions/#v1} + */ if (!suggestion) return null; - if (suggestion?.acct) return { - source: 'staff', - sources: ['featured'], - account: suggestion, - }; + if (suggestion?.acct) + return { + source: 'staff', + sources: ['featured'], + account: suggestion, + }; if (!suggestion.sources) { suggestion.sources = []; diff --git a/packages/pl-api/lib/entities/tag.ts b/packages/pl-api/lib/entities/tag.ts index 2895ee193..2363d5dc3 100644 --- a/packages/pl-api/lib/entities/tag.ts +++ b/packages/pl-api/lib/entities/tag.ts @@ -3,18 +3,24 @@ import * as v from 'valibot'; /** * @category Schemas */ -const historySchema = v.array(v.object({ - day: v.pipe(v.unknown(), v.transform(Number)), - accounts: v.pipe(v.unknown(), v.transform(Number)), - uses: v.pipe(v.unknown(), v.transform(Number)), -})); +const historySchema = v.array( + v.object({ + day: v.pipe(v.unknown(), v.transform(Number)), + accounts: v.pipe(v.unknown(), v.transform(Number)), + uses: v.pipe(v.unknown(), v.transform(Number)), + }), +); /** * @category Schemas * @see {@link https://docs.joinmastodon.org/entities/tag} */ const tagSchema = v.object({ - name: v.pipe(v.string(), v.transform(name => name.startsWith('#') ? name.slice(1) : name), v.minLength(1)), + name: v.pipe( + v.string(), + v.transform((name) => (name.startsWith('#') ? name.slice(1) : name)), + v.minLength(1), + ), url: v.fallback(v.pipe(v.string(), v.url()), ''), history: v.fallback(v.nullable(historySchema), null), following: v.fallback(v.optional(v.boolean()), undefined), diff --git a/packages/pl-api/lib/entities/translation.ts b/packages/pl-api/lib/entities/translation.ts index 08945bf36..7be26d583 100644 --- a/packages/pl-api/lib/entities/translation.ts +++ b/packages/pl-api/lib/entities/translation.ts @@ -4,9 +4,11 @@ import { filteredArray } from './utils'; const translationPollSchema = v.object({ id: v.string(), - options: v.array(v.object({ - title: v.string(), - })), + options: v.array( + v.object({ + title: v.string(), + }), + ), }); const translationMediaAttachment = v.object({ @@ -21,14 +23,15 @@ const translationMediaAttachment = v.object({ const translationSchema = v.pipe( v.any(), v.transform((translation: any) => { - /** - * handle Akkoma - * @see {@link https://akkoma.dev/AkkomaGang/akkoma/src/branch/develop/lib/pleroma/web/mastodon_api/controllers/status_controller.ex#L504} - */ - if (translation?.text) return { - content: translation.text, - detected_source_language: translation.detected_language, - }; + /** + * handle Akkoma + * @see {@link https://akkoma.dev/AkkomaGang/akkoma/src/branch/develop/lib/pleroma/web/mastodon_api/controllers/status_controller.ex#L504} + */ + if (translation?.text) + return { + content: translation.text, + detected_source_language: translation.detected_language, + }; return translation; }), diff --git a/packages/pl-api/lib/entities/utils.ts b/packages/pl-api/lib/entities/utils.ts index 3c54d0202..c131694aa 100644 --- a/packages/pl-api/lib/entities/utils.ts +++ b/packages/pl-api/lib/entities/utils.ts @@ -9,18 +9,24 @@ const datetimeSchema = v.pipe( v.regex(/^\d{4}-\d{2}-\d{2}T([01]\d|2[0-3]):[0-5]\d:[0-5]\d(\.\d+)?(([+-]\d{2}:?\d{2})|(Z)?)$/), ); -const dateSchema = v.pipe(v.string(), v.transform((value) => value.slice(0, 10)), v.regex(/^\d{4}-\d{2}-\d{2}$/)); +const dateSchema = v.pipe( + v.string(), + v.transform((value) => value.slice(0, 10)), + v.regex(/^\d{4}-\d{2}-\d{2}$/), +); /** Validates individual items in an array, dropping any that aren't valid. */ const filteredArray = (schema: v.BaseSchema>) => v.pipe( v.fallback(v.array(v.any()), []), - v.transform((arr) => ( - (arr || []).map((item) => { - const parsed = v.safeParse(schema, item); - return parsed.success ? parsed.output : undefined; - }).filter((item): item is T => Boolean(item)) - )), + v.transform((arr) => + (arr || []) + .map((item) => { + const parsed = v.safeParse(schema, item); + return parsed.success ? parsed.output : undefined; + }) + .filter((item): item is T => Boolean(item)), + ), ); /** Validates the string as an emoji. */ @@ -34,7 +40,7 @@ const coerceObject = (shape: T): v.ObjectSchema typeof input === 'object' && input !== null ? input : {}), + v.transform((input) => (typeof input === 'object' && input !== null ? input : {})), v.object(shape), ), {}, diff --git a/packages/pl-api/lib/features.ts b/packages/pl-api/lib/features.ts index efe99522d..f1f4d52f9 100644 --- a/packages/pl-api/lib/features.ts +++ b/packages/pl-api/lib/features.ts @@ -212,20 +212,13 @@ const getFeatures = (instance: Instance) => { * @see GET /api/v1/pleroma/backups * @see POST /api/v1/pleroma/backups */ - accountBackups: any([ - v.software === AKKOMA, - v.software === PLEROMA, - ]), + accountBackups: any([v.software === AKKOMA, v.software === PLEROMA]), /** * The accounts API allows an acct instead of an ID. * @see GET /api/v1/accounts/:acct_or_id */ - accountByUsername: any([ - v.software === AKKOMA, - v.software === MITRA, - v.software === PLEROMA, - ]), + accountByUsername: any([v.software === AKKOMA, v.software === MITRA, v.software === PLEROMA]), /** * Ability to create accounts. @@ -270,9 +263,7 @@ const getFeatures = (instance: Instance) => { * Ability to set one's location on their profile. * @see PATCH /api/v1/accounts/update_credentials */ - accountLocation: any([ - instance.api_versions['account_location.pleroma.pl-api'] >= 1, - ]), + accountLocation: any([instance.api_versions['account_location.pleroma.pl-api'] >= 1]), /** * Look up an account by the acct. @@ -300,18 +291,13 @@ const getFeatures = (instance: Instance) => { /** * @see PATCH /api/v1/accounts/update_credentials */ - accountMentionPolicy: any([ - v.software === MITRA, - ]), + accountMentionPolicy: any([v.software === MITRA]), /** * Move followers to a different ActivityPub account. * @see POST /api/pleroma/move_account */ - accountMoving: any([ - v.software === AKKOMA, - v.software === PLEROMA && gte(v.version, '2.5.0'), - ]), + accountMoving: any([v.software === AKKOMA, v.software === PLEROMA && gte(v.version, '2.5.0')]), /** * Ability to subscribe to notifications every time an account posts. @@ -332,25 +318,18 @@ const getFeatures = (instance: Instance) => { /** * @see PATCH /api/v1/accounts/update_credentials */ - accountWebLayout: any([ - v.software === GOTOSOCIAL && gte(v.version, '0.19.0'), - ]), + accountWebLayout: any([v.software === GOTOSOCIAL && gte(v.version, '0.19.0')]), /** - * @see PATCH /api/v1/accounts/update_credentials - */ - accountWebVisibility: any([ - v.software === GOTOSOCIAL && gte(v.version, '0.17.0'), - ]), + * @see PATCH /api/v1/accounts/update_credentials + */ + accountWebVisibility: any([v.software === GOTOSOCIAL && gte(v.version, '0.17.0')]), /** * Ability to address a status to a list of users. * @see POST /api/v1/statuses */ - addressableLists: any([ - v.software === AKKOMA, - v.software === PLEROMA, - ]), + addressableLists: any([v.software === AKKOMA, v.software === PLEROMA]), /** * @see GET /api/v1/admin/custom_emojis @@ -373,14 +352,9 @@ const getFeatures = (instance: Instance) => { * @see PATCH /api/v1/admin/instance/rules/:id * @see DELETE /api/v1/admin/instance/rules/:id */ - adminRules: any([ - v.software === GOTOSOCIAL, - v.software === PLEROMA && gte(v.version, '2.7.0'), - ]), + adminRules: any([v.software === GOTOSOCIAL, v.software === PLEROMA && gte(v.version, '2.7.0')]), - adminRulesPriority: any([ - v.software === PLEROMA && gte(v.version, '2.7.0'), - ]), + adminRulesPriority: any([v.software === PLEROMA && gte(v.version, '2.7.0')]), /** * Can display announcements set by admins. @@ -452,7 +426,8 @@ const getFeatures = (instance: Instance) => { instance.api_versions['kmyblue_bookmark_category.fedibird.pl-api'] >= 1, ]), - bookmarkFoldersMultiple: instance.api_versions['kmyblue_bookmark_category.fedibird.pl-api'] >= 1, + bookmarkFoldersMultiple: + instance.api_versions['kmyblue_bookmark_category.fedibird.pl-api'] >= 1, /** * Can bookmark statuses. @@ -541,9 +516,7 @@ const getFeatures = (instance: Instance) => { * Ability to delete a chat. * @see DELETE /api/v1/pleroma/chats/:id */ - chatsDelete: any([ - instance.api_versions['chat_delete.pleroma.pl-api'] >= 1, - ]), + chatsDelete: any([instance.api_versions['chat_delete.pleroma.pl-api'] >= 1]), circles: instance.api_versions['kmyblue_circle_history.fedibird.pl-api'] >= 1, @@ -576,10 +549,7 @@ const getFeatures = (instance: Instance) => { /** * @see GET /api/v1/conversations */ - conversationsByRecipients: any([ - v.software === AKKOMA, - v.software === PLEROMA, - ]), + conversationsByRecipients: any([v.software === AKKOMA, v.software === PLEROMA]), /** * Ability to post statuses to the recipients of parent post. @@ -590,10 +560,7 @@ const getFeatures = (instance: Instance) => { /** * @see POST /api/v1/statuses */ - createStatusExpiration: any([ - v.software === AKKOMA, - v.software === PLEROMA, - ]), + createStatusExpiration: any([v.software === AKKOMA, v.software === PLEROMA]), /** * Ability to address recipients of a status explicitly (with `to`). @@ -607,19 +574,13 @@ const getFeatures = (instance: Instance) => { /** * @see POST /api/v1/statuses */ - createStatusReplyToConversation: any([ - v.software === AKKOMA, - v.software === PLEROMA, - ]), + createStatusReplyToConversation: any([v.software === AKKOMA, v.software === PLEROMA]), /** * Ability to address a status to a list of users. * @see POST /api/v1/statuses */ - createStatusListScope: any([ - v.software === AKKOMA, - v.software === PLEROMA, - ]), + createStatusListScope: any([v.software === AKKOMA, v.software === PLEROMA]), /** * @see POST /api/v1/statuses @@ -646,11 +607,7 @@ const getFeatures = (instance: Instance) => { * @see POST /api/v1/statuses * @see POST /api/v1/statuses/preview */ - createStatusPreview: any([ - v.software === AKKOMA, - v.software === PLEROMA, - v.software === MITRA, - ]), + createStatusPreview: any([v.software === AKKOMA, v.software === PLEROMA, v.software === MITRA]), /** * Ability to add non-standard reactions to a status. @@ -666,11 +623,7 @@ const getFeatures = (instance: Instance) => { * @see POST /api/v1/accounts/delete * @see POST /api/pleroma/delete_account */ - deleteAccount: any([ - v.software === AKKOMA, - v.software === GOTOSOCIAL, - v.software === PLEROMA, - ]), + deleteAccount: any([v.software === AKKOMA, v.software === GOTOSOCIAL, v.software === PLEROMA]), /** * @see POST /api/v1/settings/delete_account @@ -782,9 +735,9 @@ const getFeatures = (instance: Instance) => { events: instance.api_versions['events.pleroma.pl-api'] >= 1, /** - * Export blocks to CSV file - * @see GET /api/v1/exports/blocks.csv - */ + * Export blocks to CSV file + * @see GET /api/v1/exports/blocks.csv + */ exportBlocks: v.software === GOTOSOCIAL && gte(v.version, '0.17.0'), /** @@ -808,15 +761,15 @@ const getFeatures = (instance: Instance) => { ]), /** - * Export lists to CSV file - * @see GET /api/v1/exports/lists.csv - */ + * Export lists to CSV file + * @see GET /api/v1/exports/lists.csv + */ exportLists: v.software === GOTOSOCIAL && gte(v.version, '0.17.0'), /** - * Export mutes to CSV file - * @see GET /api/v1/exports/mutes.csv - */ + * Export mutes to CSV file + * @see GET /api/v1/exports/mutes.csv + */ exportMutes: v.software === GOTOSOCIAL && gte(v.version, '0.17.0'), /** Whether the accounts who favourited or emoji-reacted to a status can be viewed through the API. */ @@ -897,10 +850,7 @@ const getFeatures = (instance: Instance) => { * Allows setting the focal point of a media attachment. * @see {@link https://docs.joinmastodon.org/methods/media/} */ - focalPoint: any([ - v.software === GOTOSOCIAL, - v.software === MASTODON, - ]), + focalPoint: any([v.software === GOTOSOCIAL, v.software === MASTODON]), /** * TODO @@ -1002,10 +952,7 @@ const getFeatures = (instance: Instance) => { * @see POST /api/v1/admin/groups/:group_id/unsuspend * @see DELETE /api/v1/admin/groups/:group_id */ - groups: any([ - v.software === PIXELFED, - instance.api_versions['groups.pleroma.pl-api'] >= 1, - ]), + groups: any([v.software === PIXELFED, instance.api_versions['groups.pleroma.pl-api'] >= 1]), groupsSlugs: instance.api_versions['groups.pleroma.pl-api'] >= 1, @@ -1019,6 +966,8 @@ const getFeatures = (instance: Instance) => { v.software === PLEROMA, ]), + importArchive: false, + /** * Import a .csv file with a list of blocked users. * @see POST /api/pleroma/blocks_import @@ -1034,9 +983,7 @@ const getFeatures = (instance: Instance) => { * Move followers from remote alias. * @see POST /api/v1/settings/import_followers */ - importFollowers: any([ - v.software === MITRA && gte(v.version, '2.18.0'), - ]), + importFollowers: any([v.software === MITRA && gte(v.version, '2.18.0')]), /** * Import a .csv file with a list of followed users. @@ -1081,7 +1028,7 @@ const getFeatures = (instance: Instance) => { * Mastodon server information API v2. * @see GET /api/v2/instance * @see {@link https://docs.joinmastodon.org/methods/instance/#v2} - */ + */ instanceV2: any([ v.software === FIREFISH, v.software === GOTOSOCIAL, @@ -1171,10 +1118,9 @@ const getFeatures = (instance: Instance) => { * Ability to post statuses that don't federate. * @see POST /api/v1/statuses */ - localOnlyStatuses: federation && any([ - v.software === GOTOSOCIAL, - v.software === MASTODON && v.build === HOMETOWN, - ]), + localOnlyStatuses: + federation && + any([v.software === GOTOSOCIAL, v.software === MASTODON && v.build === HOMETOWN]), /** * Can sign in using username instead of e-mail address. @@ -1192,10 +1138,7 @@ const getFeatures = (instance: Instance) => { * @see GET /api/pleroma/aliases * @see PATCH /api/v1/accounts/update_credentials */ - manageAccountAliases: any([ - v.software === AKKOMA, - v.software === PLEROMA, - ]), + manageAccountAliases: any([v.software === AKKOMA, v.software === PLEROMA]), /** * @see GET /api/pleroma/accounts/mfa @@ -1212,18 +1155,12 @@ const getFeatures = (instance: Instance) => { /** * @see GET /api/pleroma/accounts/mfa/backup_codes */ - manageMfaBackupCodes: any([ - v.software === AKKOMA, - v.software === PLEROMA, - ]), + manageMfaBackupCodes: any([v.software === AKKOMA, v.software === PLEROMA]), /** * @see POST /api/v1/user/2fa/enable */ - manageMfaRequiresPassword: any([ - v.software === AKKOMA, - v.software === PLEROMA, - ]), + manageMfaRequiresPassword: any([v.software === AKKOMA, v.software === PLEROMA]), /** * Can perform moderation actions with account and reports. @@ -1288,10 +1225,7 @@ const getFeatures = (instance: Instance) => { * Ability to hide notifications from people you don't follow. * @see PUT /api/pleroma/notification_settings */ - muteStrangers: any([ - v.software === AKKOMA, - v.software === PLEROMA, - ]), + muteStrangers: any([v.software === AKKOMA, v.software === PLEROMA]), /** * Ability to mute users. @@ -1347,10 +1281,7 @@ const getFeatures = (instance: Instance) => { /** * @see DELETE /api/v1/notifications/destroy_multiple */ - notificationsDismissMultiple: any([ - v.software === AKKOMA, - v.software === PLEROMA, - ]), + notificationsDismissMultiple: any([v.software === AKKOMA, v.software === PLEROMA]), /** * @see GET /api/v1/notifications @@ -1360,10 +1291,7 @@ const getFeatures = (instance: Instance) => { /** * @see GET /api/v1/notifications */ - notificationsExcludeVisibilities: any([ - v.software === AKKOMA, - v.software === PLEROMA, - ]), + notificationsExcludeVisibilities: any([v.software === AKKOMA, v.software === PLEROMA]), /** * @see GET /api/v1/notifications/unread_count @@ -1409,10 +1337,7 @@ const getFeatures = (instance: Instance) => { instance.api_versions['outgoing_follow_requests.pleroma.pl-api'] >= 1, ]), - pleromaAdminAccounts: any([ - v.software === AKKOMA, - v.software === PLEROMA, - ]), + pleromaAdminAccounts: any([v.software === AKKOMA, v.software === PLEROMA]), /** * Ability to manage announcements by admins. @@ -1423,25 +1348,13 @@ const getFeatures = (instance: Instance) => { * @see DELETE /api/v1/pleroma/admin/announcements/:id * @see {@link https://docs.pleroma.social/backend/development/API/admin_api/#get-apiv1pleromaadminannouncements} */ - pleromaAdminAnnouncements: any([ - v.software === AKKOMA, - v.software === PLEROMA, - ]), + pleromaAdminAnnouncements: any([v.software === AKKOMA, v.software === PLEROMA]), - pleromaAdminModerationLog: any([ - v.software === AKKOMA, - v.software === PLEROMA, - ]), + pleromaAdminModerationLog: any([v.software === AKKOMA, v.software === PLEROMA]), - pleromaAdminRelays: any([ - v.software === AKKOMA, - v.software === PLEROMA, - ]), + pleromaAdminRelays: any([v.software === AKKOMA, v.software === PLEROMA]), - pleromaAdminStatuses: any([ - v.software === AKKOMA, - v.software === PLEROMA, - ]), + pleromaAdminStatuses: any([v.software === AKKOMA, v.software === PLEROMA]), pleromaAdminStatusesRedact: instance.api_versions['admin_statuses_redact.pleroma.pl-api'] >= 1, @@ -1449,10 +1362,7 @@ const getFeatures = (instance: Instance) => { * Displays a form to follow a user when logged out. * @see POST /main/ostatus */ - pleromaRemoteFollow: any([ - v.software === AKKOMA, - v.software === PLEROMA, - ]), + pleromaRemoteFollow: any([v.software === AKKOMA, v.software === PLEROMA]), /** * Can add polls to statuses. @@ -1531,10 +1441,7 @@ const getFeatures = (instance: Instance) => { * Returns favorites timeline of any user * @see GET /api/v1/pleroma/accounts/:id/favourites */ - publicFavourites: any([ - v.software === AKKOMA, - v.software === PLEROMA, - ]), + publicFavourites: any([v.software === AKKOMA, v.software === PLEROMA]), /** * Can display a timeline of all known public statuses. @@ -1611,10 +1518,7 @@ const getFeatures = (instance: Instance) => { * Can request a password reset email through the API. * @see POST /auth/password */ - resetPassword: any([ - v.software === AKKOMA, - v.software === PLEROMA, - ]), + resetPassword: any([v.software === AKKOMA, v.software === PLEROMA]), /** * Ability to post statuses in Markdown, BBCode, and HTML. @@ -1772,9 +1676,7 @@ const getFeatures = (instance: Instance) => { * Remove an account from follow suggestions * @see DELETE /api/v1/suggestions/:account_id */ - suggestionsDismiss: any([ - v.software === MASTODON, - ]), + suggestionsDismiss: any([v.software === MASTODON]), /** * Supports V2 suggested accounts. @@ -1844,11 +1746,7 @@ const getFeatures = (instance: Instance) => { * Whether the backend allows adding users you don't follow to lists. * @see POST /api/v1/lists/:id/accounts */ - unrestrictedLists: any([ - v.software === AKKOMA, - v.software === PLEROMA, - v.software === SNAC, - ]), + unrestrictedLists: any([v.software === AKKOMA, v.software === PLEROMA, v.software === SNAC]), /** * Can display a timeline of most recently wrenched statuses. @@ -1879,9 +1777,12 @@ const parseVersion = (version: string): Backend => { const match = regex.exec(version.replace('/', ' ')); const semverString = match && (match[3] || match[1]); - const semver = match ? semverParse(semverString) || semverCoerce(semverString, { - loose: true, - }) : null; + const semver = match + ? semverParse(semverString) || + semverCoerce(semverString, { + loose: true, + }) + : null; const compat = match ? semverParse(match[1]) || semverCoerce(match[1]) : null; if (match && semver && compat) { return { @@ -1890,16 +1791,15 @@ const parseVersion = (version: string): Backend => { software: match[2] || MASTODON, version: semver.version.split('-')[0], }; - } else { - // If we can't parse the version, this is a new and exotic backend. - // Fall back to minimal featureset. - return { - build: null, - compatVersion: '0.0.0', - software: null, - version: '0.0.0', - }; } + // If we can't parse the version, this is a new and exotic backend. + // Fall back to minimal featureset. + return { + build: null, + compatVersion: '0.0.0', + software: null, + version: '0.0.0', + }; }; export { diff --git a/packages/pl-api/lib/main.ts b/packages/pl-api/lib/main.ts index 9d4abc3d4..639273eae 100644 --- a/packages/pl-api/lib/main.ts +++ b/packages/pl-api/lib/main.ts @@ -1,4 +1,4 @@ -export { PlApiClient } from './client'; +export { default as PlApiClient } from './client'; export { PlApiDirectoryClient } from './directory-client'; export { type Response as PlApiResponse } from './request'; export * from './entities'; diff --git a/packages/pl-api/lib/params/accounts.ts b/packages/pl-api/lib/params/accounts.ts index 37c1dc365..e809b7757 100644 --- a/packages/pl-api/lib/params/accounts.ts +++ b/packages/pl-api/lib/params/accounts.ts @@ -1,4 +1,11 @@ -import type { LanguageParam, OnlyEventsParam, OnlyMediaParam, PaginationParams, WithMutedParam, WithRelationshipsParam } from './common'; +import type { + LanguageParam, + OnlyEventsParam, + OnlyMediaParam, + PaginationParams, + WithMutedParam, + WithRelationshipsParam, +} from './common'; /** * @category Request params @@ -8,7 +15,8 @@ type GetAccountParams = WithMutedParam; /** * @category Request params */ -interface GetAccountStatusesParams extends PaginationParams, WithMutedParam, OnlyEventsParam, OnlyMediaParam, LanguageParam { +interface GetAccountStatusesParams + extends PaginationParams, WithMutedParam, OnlyEventsParam, OnlyMediaParam, LanguageParam { /** Boolean. Filter out statuses in reply to a different account. */ exclude_replies?: boolean; /** Boolean. Filter out boosts from the response. */ @@ -48,7 +56,7 @@ interface FollowAccountParams { /** * Array of String (ISO 639-1 language two-letter code). Filter received statuses for these languages. If not provided, you will receive this account’s posts in all languages. * Requires features{@link Features['followAccountLanguages']}. - */ + */ languages?: string[]; } diff --git a/packages/pl-api/lib/params/admin.ts b/packages/pl-api/lib/params/admin.ts index b320ad458..51dd152b2 100644 --- a/packages/pl-api/lib/params/admin.ts +++ b/packages/pl-api/lib/params/admin.ts @@ -130,7 +130,16 @@ type AdminGetCanonicalEmailBlocks = PaginationParams; /** * @category Request params */ -type AdminDimensionKey = 'languages' | 'sources' | 'servers' | 'space_usage' | 'software_versions' | 'tag_servers' | 'tag_languages' | 'instance_accounts' | 'instance_languages'; +type AdminDimensionKey = + | 'languages' + | 'sources' + | 'servers' + | 'space_usage' + | 'software_versions' + | 'tag_servers' + | 'tag_languages' + | 'instance_accounts' + | 'instance_languages'; /** * @category Request params @@ -197,7 +206,21 @@ type AdminUpdateIpBlockParams = Partial; /** * @category Request params */ -type AdminMeasureKey = 'active_users' | 'new_users' | 'interactions' | 'opened_reports' | 'resolved_reports' | 'tag_accounts' | 'tag_uses' | 'tag_servers' | 'instance_accounts' | 'instance_media_attachments' | 'instance_reports' | 'instance_statuses' | 'instance_follows' | 'instance_followers'; +type AdminMeasureKey = + | 'active_users' + | 'new_users' + | 'interactions' + | 'opened_reports' + | 'resolved_reports' + | 'tag_accounts' + | 'tag_uses' + | 'tag_servers' + | 'instance_accounts' + | 'instance_media_attachments' + | 'instance_reports' + | 'instance_statuses' + | 'instance_follows' + | 'instance_followers'; /** * @category Request params @@ -333,8 +356,7 @@ interface AdminUpdateCustomEmojiParams { /** * @category Request params */ -interface AdminGetGroupsParams { -} +interface AdminGetGroupsParams {} export type { AdminGetAccountsParams, diff --git a/packages/pl-api/lib/params/antennas.ts b/packages/pl-api/lib/params/antennas.ts index 6af57834d..8293565a0 100644 --- a/packages/pl-api/lib/params/antennas.ts +++ b/packages/pl-api/lib/params/antennas.ts @@ -17,7 +17,4 @@ interface CreateAntennaParams { */ type UpdateAntennaParams = Partial; -export { - type CreateAntennaParams, - type UpdateAntennaParams, -}; +export { type CreateAntennaParams, type UpdateAntennaParams }; diff --git a/packages/pl-api/lib/params/apps.ts b/packages/pl-api/lib/params/apps.ts index 17e5a839d..367a17fa6 100644 --- a/packages/pl-api/lib/params/apps.ts +++ b/packages/pl-api/lib/params/apps.ts @@ -12,6 +12,4 @@ interface CreateApplicationParams { website?: string; } -export type { - CreateApplicationParams, -}; +export type { CreateApplicationParams }; diff --git a/packages/pl-api/lib/params/chats.ts b/packages/pl-api/lib/params/chats.ts index c9e16a5db..348f392eb 100644 --- a/packages/pl-api/lib/params/chats.ts +++ b/packages/pl-api/lib/params/chats.ts @@ -13,16 +13,14 @@ type GetChatMessagesParams = PaginationParams; /** * @category Request params */ -type CreateChatMessageParams = { - content?: string; - media_id: string; -} | { - content: string; - media_id?: string; -}; +type CreateChatMessageParams = + | { + content?: string; + media_id: string; + } + | { + content: string; + media_id?: string; + }; -export type { - GetChatsParams, - GetChatMessagesParams, - CreateChatMessageParams, -}; +export type { GetChatsParams, GetChatMessagesParams, CreateChatMessageParams }; diff --git a/packages/pl-api/lib/params/circles.ts b/packages/pl-api/lib/params/circles.ts index 249614889..422f3ddb6 100644 --- a/packages/pl-api/lib/params/circles.ts +++ b/packages/pl-api/lib/params/circles.ts @@ -10,7 +10,4 @@ type GetCircleStatusesParams = PaginationParams; */ type GetCircleAccountsParams = PaginationParams; -export type { - GetCircleStatusesParams, - GetCircleAccountsParams, -}; +export type { GetCircleStatusesParams, GetCircleAccountsParams }; diff --git a/packages/pl-api/lib/params/instance.ts b/packages/pl-api/lib/params/instance.ts index 4d5652573..acd6f46d3 100644 --- a/packages/pl-api/lib/params/instance.ts +++ b/packages/pl-api/lib/params/instance.ts @@ -12,6 +12,4 @@ interface ProfileDirectoryParams { local?: boolean; } -export type { - ProfileDirectoryParams, -}; +export type { ProfileDirectoryParams }; diff --git a/packages/pl-api/lib/params/interaction-requests.ts b/packages/pl-api/lib/params/interaction-requests.ts index b5c53fac5..cb06ce92a 100644 --- a/packages/pl-api/lib/params/interaction-requests.ts +++ b/packages/pl-api/lib/params/interaction-requests.ts @@ -14,6 +14,4 @@ interface GetInteractionRequestsParams extends PaginationParams { reblogs?: boolean; } -export type { - GetInteractionRequestsParams, -}; +export type { GetInteractionRequestsParams }; diff --git a/packages/pl-api/lib/params/lists.ts b/packages/pl-api/lib/params/lists.ts index 2536302a6..520a174c5 100644 --- a/packages/pl-api/lib/params/lists.ts +++ b/packages/pl-api/lib/params/lists.ts @@ -13,12 +13,12 @@ interface CreateListParams { /** * Boolean. Whether to receive notifications for new posts in the list. * Requires features{@link Features['listsNotifications']} - */ + */ notify?: boolean; /** * Boolean. Whether the list should appear in the navigation bar. * Requires features{@link Features['listsFavourites']} - */ + */ favourite?: boolean; } @@ -32,8 +32,4 @@ type UpdateListParams = Partial; */ type GetListAccountsParams = PaginationParams; -export type { - CreateListParams, - UpdateListParams, - GetListAccountsParams, -}; +export type { CreateListParams, UpdateListParams, GetListAccountsParams }; diff --git a/packages/pl-api/lib/params/media.ts b/packages/pl-api/lib/params/media.ts index 973c0a13e..caedfcd12 100644 --- a/packages/pl-api/lib/params/media.ts +++ b/packages/pl-api/lib/params/media.ts @@ -30,7 +30,4 @@ interface UpdateMediaParams { focus?: string; } -export type { - UploadMediaParams, - UpdateMediaParams, -}; +export type { UploadMediaParams, UpdateMediaParams }; diff --git a/packages/pl-api/lib/params/oauth.ts b/packages/pl-api/lib/params/oauth.ts index 354540f8a..a9013674e 100644 --- a/packages/pl-api/lib/params/oauth.ts +++ b/packages/pl-api/lib/params/oauth.ts @@ -60,9 +60,4 @@ interface MfaChallengeParams { code: string; } -export type { - OauthAuthorizeParams, - GetTokenParams, - RevokeTokenParams, - MfaChallengeParams, -}; +export type { OauthAuthorizeParams, GetTokenParams, RevokeTokenParams, MfaChallengeParams }; diff --git a/packages/pl-api/lib/params/scheduled-statuses.ts b/packages/pl-api/lib/params/scheduled-statuses.ts index 4fdb0fa5b..dee2ef8d3 100644 --- a/packages/pl-api/lib/params/scheduled-statuses.ts +++ b/packages/pl-api/lib/params/scheduled-statuses.ts @@ -5,6 +5,4 @@ import type { PaginationParams } from './common'; */ type GetScheduledStatusesParams = PaginationParams; -export type { - GetScheduledStatusesParams, -}; +export type { GetScheduledStatusesParams }; diff --git a/packages/pl-api/lib/params/search.ts b/packages/pl-api/lib/params/search.ts index 4f3df33ea..ac54f6cd0 100644 --- a/packages/pl-api/lib/params/search.ts +++ b/packages/pl-api/lib/params/search.ts @@ -18,6 +18,4 @@ interface SearchParams extends Exclude, WithRelati offset?: number; } -export type { - SearchParams, -}; +export type { SearchParams }; diff --git a/packages/pl-api/lib/params/settings.ts b/packages/pl-api/lib/params/settings.ts index 28159eea2..94d1633d0 100644 --- a/packages/pl-api/lib/params/settings.ts +++ b/packages/pl-api/lib/params/settings.ts @@ -38,12 +38,15 @@ type CreateAccountParams = { /** Invite code */ invite_code?: string; -} & ({ - /** EIP-4361 message */ - message: string; - /** EIP-4361 signature (required if message is present) */ - signature: string; -} | {}) +} & ( + | { + /** EIP-4361 message */ + message: string; + /** EIP-4361 signature (required if message is present) */ + signature: string; + } + | {} +); /** * @category Request params @@ -71,12 +74,15 @@ interface UpdateCredentialsParams { /** Boolean. Whether public posts should be searchable to anyone. */ indexable?: boolean; /** Hash. The profile fields to be set. Inside this hash, the key is an integer cast to a string (although the exact integer does not matter), and the value is another hash including name and value. By default, max 4 fields. */ - fields_attributes?: Record; + fields_attributes?: Record< + string, + { + /** String. The name of the profile field. By default, max 255 characters. */ + name: string; + /** String. The value of the profile field. By default, max 255 characters. */ + value: string; + } + >; source?: { /** String. Default post privacy for authored statuses. Can be `public`, `unlisted`, or `private`. */ privacy?: string; @@ -140,7 +146,7 @@ interface UpdateCredentialsParams { /** * Enable RSS feed for this account's Public posts at `/[username]/feed.rss` * Requires features{@link Features.accountEnableRss}. - */ + */ enable_rss?: boolean; /** * Include boosts created by the account on the web view of the account. @@ -199,7 +205,9 @@ type UpdateInteractionPoliciesParams = Record< 'can_favourite' | 'can_reblog' | 'can_reply', Record< 'always' | 'with_approval', - Array<'public' | 'followers' | 'following' | 'mutuals' | 'mentioned' | 'author' | 'me' | string> + Array< + 'public' | 'followers' | 'following' | 'mutuals' | 'mentioned' | 'author' | 'me' | string + > > > >; diff --git a/packages/pl-api/lib/params/statuses.ts b/packages/pl-api/lib/params/statuses.ts index 5b1f27b6b..9a8ef8e9c 100644 --- a/packages/pl-api/lib/params/statuses.ts +++ b/packages/pl-api/lib/params/statuses.ts @@ -71,7 +71,7 @@ interface CreateStatusOptionalParams { /** * A list of nicknames (like `lain@soykaf.club` or `lain` on the local server) that will be used to determine who is going to be addressed by this post. Using this will disable the implicit addressing by mentioned names in the `status` body, only the people in the `to` list will be addressed. The normal rules for post visibility are not affected by this and will still apply. * Requires features{@link Features['createStatusExplicitAddressing']}. - */ + */ to?: string[]; /** * The number of seconds the posted activity should expire in. When a posted activity expires it will be deleted from the server, and a delete request for it will be federated. This needs to be longer than an hour. @@ -116,7 +116,8 @@ interface CreateStatusOptionalParams { /** * @category Request params */ -type CreateStatusParams = (CreateStatusWithContent | CreateStatusWithMedia) & CreateStatusOptionalParams; +type CreateStatusParams = (CreateStatusWithContent | CreateStatusWithMedia) & + CreateStatusOptionalParams; /** * @category Request params @@ -144,22 +145,26 @@ type GetStatusContextParams = LanguageParam; /** * @category Request params */ -type GetRebloggedByParams = Omit +type GetRebloggedByParams = Omit; /** * @category Request params */ -type GetFavouritedByParams = Omit +type GetFavouritedByParams = Omit; /** * @category Request params */ -type EditStatusOptionalParams = Pick; +type EditStatusOptionalParams = Pick< + CreateStatusOptionalParams, + 'content_type' | 'sensitive' | 'spoiler_text' | 'language' | 'quote_approval_policy' +>; /** * @category Request params */ -type EditStatusParams = (CreateStatusWithContent | CreateStatusWithMedia) & EditStatusOptionalParams; +type EditStatusParams = (CreateStatusWithContent | CreateStatusWithMedia) & + EditStatusOptionalParams; /** * @category Request params @@ -199,4 +204,3 @@ export type { GetStatusReferencesParams, GetStatusMentionedUsersParams, }; - diff --git a/packages/pl-api/lib/params/stories.ts b/packages/pl-api/lib/params/stories.ts index eb68782aa..fa054eb85 100644 --- a/packages/pl-api/lib/params/stories.ts +++ b/packages/pl-api/lib/params/stories.ts @@ -7,7 +7,15 @@ interface CreateStoryPollParams { can_react: boolean; } -type StoryReportType = 'spam' | 'sensitive' | 'abusive' | 'underage' | 'copyright' | 'impersonation' | 'scam' | 'terrorism'; +type StoryReportType = + | 'spam' + | 'sensitive' + | 'abusive' + | 'underage' + | 'copyright' + | 'impersonation' + | 'scam' + | 'terrorism'; interface CropStoryPhotoParams { width: number; diff --git a/packages/pl-api/lib/params/timelines.ts b/packages/pl-api/lib/params/timelines.ts index 4beba8590..a8892d115 100644 --- a/packages/pl-api/lib/params/timelines.ts +++ b/packages/pl-api/lib/params/timelines.ts @@ -1,9 +1,16 @@ -import type { LanguageParam, OnlyEventsParam, OnlyMediaParam, PaginationParams, WithMutedParam } from './common'; +import type { + LanguageParam, + OnlyEventsParam, + OnlyMediaParam, + PaginationParams, + WithMutedParam, +} from './common'; /** * @category Request params */ -interface PublicTimelineParams extends PaginationParams, WithMutedParam, OnlyEventsParam, OnlyMediaParam, LanguageParam { +interface PublicTimelineParams + extends PaginationParams, WithMutedParam, OnlyEventsParam, OnlyMediaParam, LanguageParam { /** Boolean. Show only local statuses? Defaults to false. */ local?: boolean; /** Boolean. Show only remote statuses? Defaults to false. */ @@ -19,7 +26,8 @@ interface PublicTimelineParams extends PaginationParams, WithMutedParam, OnlyEve /** * @category Request params */ -interface HashtagTimelineParams extends PaginationParams, WithMutedParam, OnlyEventsParam, OnlyMediaParam, LanguageParam { +interface HashtagTimelineParams + extends PaginationParams, WithMutedParam, OnlyEventsParam, OnlyMediaParam, LanguageParam { /** Array of String. Return statuses that contain any of these additional tags. */ any?: string[]; /** Array of String. Return statuses that contain all of these additional tags. */ @@ -85,12 +93,20 @@ type GroupTimelineParams = PaginationParams & WithMutedParam & OnlyMediaParam & /** * @category Request params */ -type BubbleTimelineParams = PaginationParams & WithMutedParam & OnlyEventsParam & OnlyMediaParam & LanguageParam; +type BubbleTimelineParams = PaginationParams & + WithMutedParam & + OnlyEventsParam & + OnlyMediaParam & + LanguageParam; /** * @category Request params */ -type WrenchedTimelineParams = PaginationParams & WithMutedParam & OnlyEventsParam & OnlyMediaParam & LanguageParam; +type WrenchedTimelineParams = PaginationParams & + WithMutedParam & + OnlyEventsParam & + OnlyMediaParam & + LanguageParam; export type { PublicTimelineParams, diff --git a/packages/pl-api/lib/params/trends.ts b/packages/pl-api/lib/params/trends.ts index 9f5cc6680..2c643f11c 100644 --- a/packages/pl-api/lib/params/trends.ts +++ b/packages/pl-api/lib/params/trends.ts @@ -30,8 +30,4 @@ interface GetTrendingStatuses extends GetTrends { */ type GetTrendingLinks = GetTrends; -export type { - GetTrendingTags, - GetTrendingStatuses, - GetTrendingLinks, -}; +export type { GetTrendingTags, GetTrendingStatuses, GetTrendingLinks }; diff --git a/packages/pl-api/lib/request.ts b/packages/pl-api/lib/request.ts index fa3577cc8..e553a67df 100644 --- a/packages/pl-api/lib/request.ts +++ b/packages/pl-api/lib/request.ts @@ -25,10 +25,10 @@ const getLinks = (response: Pick): LinkHeader => new LinkHeader(response.headers?.get('link') || undefined); const getNextLink = (response: Pick): string | null => - getLinks(response).refs.find(link => link.rel.toLocaleLowerCase() === 'next')?.uri || null; + getLinks(response).refs.find((link) => link.rel.toLocaleLowerCase() === 'next')?.uri || null; const getPrevLink = (response: Pick): string | null => - getLinks(response).refs.find(link => link.rel.toLocaleLowerCase() === 'prev')?.uri || null; + getLinks(response).refs.find((link) => link.rel.toLocaleLowerCase() === 'prev')?.uri || null; interface AsyncRefreshHeader { id: string; @@ -56,7 +56,7 @@ const getAsyncRefreshHeader = (response: Pick): AsyncRefres if (val.startsWith('"')) { typedValue = val.slice(1, -1); } else { - typedValue = parseInt(val); + typedValue = parseInt(val, 10); } asyncRefreshHeader[key] = typedValue; @@ -82,22 +82,31 @@ interface RequestBody> { type RequestMeta = Pick; -function request(this: Pick, input: URL | RequestInfo, { - body, - method = body ? 'POST' : 'GET', - params, - onUploadProgress, - signal, - contentType = 'application/json', - idempotencyKey, -}: RequestBody = {}) { +function request( + this: Pick< + PlApiClient, + 'accessToken' | 'customAuthorizationToken' | 'iceshrimpAccessToken' | 'baseURL' + >, + input: URL | RequestInfo, + { + body, + method = body ? 'POST' : 'GET', + params, + onUploadProgress, + signal, + contentType = 'application/json', + idempotencyKey, + }: RequestBody = {}, +) { input = input.toString(); const fullPath = buildFullPath(input, this.baseURL, params); const headers = new Headers(); - if (input.startsWith('/api/iceshrimp/') && this.iceshrimpAccessToken) headers.set('Authorization', `Bearer ${this.iceshrimpAccessToken}`); + if (input.startsWith('/api/iceshrimp/') && this.iceshrimpAccessToken) + headers.set('Authorization', `Bearer ${this.iceshrimpAccessToken}`); else if (this.accessToken) headers.set('Authorization', `Bearer ${this.accessToken}`); - else if (this.customAuthorizationToken) headers.set('Authorization', this.customAuthorizationToken); + else if (this.customAuthorizationToken) + headers.set('Authorization', this.customAuthorizationToken); if (contentType !== '' && body) headers.set('Content-Type', contentType); if (idempotencyKey) headers.set('Idempotency-Key', idempotencyKey); @@ -117,19 +126,21 @@ function request(this: Pick= 400) reject({ response: { - status: xhr.status, - statusText: xhr.statusText, - url: xhr.responseURL, - data, - json, - - } }); - resolve({ status: xhr.status, data, json } as any as Response); + if (xhr.status >= 400) + reject({ + response: { + status: xhr.status, + statusText: xhr.statusText, + url: xhr.responseURL, + data, + json, + }, + }); + else resolve({ status: xhr.status, data, json } as any as Response); }); xhr.open(method, fullPath, true); @@ -157,9 +168,19 @@ function request(this: Pick { total?: number; } -export type { - PaginatedResponse, -}; +export type { PaginatedResponse }; diff --git a/packages/pl-api/lib/utils/accounts.ts b/packages/pl-api/lib/utils/accounts.ts index 6a87b2a2d..83cd29322 100644 --- a/packages/pl-api/lib/utils/accounts.ts +++ b/packages/pl-api/lib/utils/accounts.ts @@ -10,7 +10,11 @@ const DEFAULT_HEADERS: Array = [ ]; /** Check if the avatar is a default avatar */ -const isDefaultHeader = (url: string = '') => url === '' || DEFAULT_HEADERS.some(header => typeof header === 'string' ? url.endsWith(header) : header.test(url)); +const isDefaultHeader = (url: string = '') => + url === '' || + DEFAULT_HEADERS.some((header) => + typeof header === 'string' ? url.endsWith(header) : header.test(url), + ); /** Default avatar filenames from various backends */ const DEFAULT_AVATARS: Array = [ @@ -23,9 +27,10 @@ const DEFAULT_AVATARS: Array = [ ]; /** Check if the avatar is a default avatar */ -const isDefaultAvatar = (url: string = '') => url === '' || DEFAULT_AVATARS.some(avatar => typeof avatar === 'string' ? url.endsWith(avatar) : avatar.test(url)); +const isDefaultAvatar = (url: string = '') => + url === '' || + DEFAULT_AVATARS.some((avatar) => + typeof avatar === 'string' ? url.endsWith(avatar) : avatar.test(url), + ); -export { - isDefaultHeader, - isDefaultAvatar, -}; +export { isDefaultHeader, isDefaultAvatar }; diff --git a/packages/pl-api/lib/utils/domain.ts b/packages/pl-api/lib/utils/domain.ts index 111fcc863..11e2b4b70 100644 --- a/packages/pl-api/lib/utils/domain.ts +++ b/packages/pl-api/lib/utils/domain.ts @@ -15,9 +15,8 @@ const guessFqn = (account: Pick): string => { if (domain) { return acct; - } else { - return [user, getDomainFromURL(account)].join('@'); } + return [user, getDomainFromURL(account)].join('@'); }; export { getDomainFromURL, guessFqn }; diff --git a/packages/pl-api/lib/utils/url.ts b/packages/pl-api/lib/utils/url.ts index d9cc6c4d9..49a2d9fa5 100644 --- a/packages/pl-api/lib/utils/url.ts +++ b/packages/pl-api/lib/utils/url.ts @@ -3,12 +3,12 @@ import queryString from 'query-string'; // Adapted from Axios https://github.com/axios/axios/blob/v1.x/lib/core/buildFullPath.js const isAbsoluteURL = (url: string) => /^([a-z][a-z\d+\-.]*:)?\/\//i.test(url); -const combineURLs = (baseURL: string, relativeURL: string) => relativeURL - ? baseURL.replace(/\/?\/$/, '') + '/' + relativeURL.replace(/^\/+/, '') - : baseURL; +const combineURLs = (baseURL: string, relativeURL: string) => + relativeURL ? baseURL.replace(/\/?\/$/, '') + '/' + relativeURL.replace(/^\/+/, '') : baseURL; const buildFullPath = (requestedURL: string, baseURL?: string, params?: Record) => { - const path = (baseURL && !isAbsoluteURL(requestedURL)) ? combineURLs(baseURL, requestedURL) : requestedURL; + const path = + baseURL && !isAbsoluteURL(requestedURL) ? combineURLs(baseURL, requestedURL) : requestedURL; if (params && Object.entries(params).length) { const { url, query } = queryString.parseUrl(path); @@ -18,6 +18,4 @@ const buildFullPath = (requestedURL: string, baseURL?: string, params?: Record