diff --git a/packages/calckey-js/.eslintrc.js b/packages/calckey-js/.eslintrc.js index 426894947b..164cf1fbe8 100644 --- a/packages/calckey-js/.eslintrc.js +++ b/packages/calckey-js/.eslintrc.js @@ -1,57 +1,65 @@ module.exports = { root: true, - parser: '@typescript-eslint/parser', + parser: "@typescript-eslint/parser", parserOptions: { tsconfigRootDir: __dirname, - project: ['./tsconfig.json'], + project: ["./tsconfig.json"], }, - plugins: [ - '@typescript-eslint', - ], - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - ], + plugins: ["@typescript-eslint"], + extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], rules: { - 'indent': ['error', 'tab', { - 'SwitchCase': 1, - 'MemberExpression': 'off', - 'flatTernaryExpressions': true, - 'ArrayExpression': 'first', - 'ObjectExpression': 'first', - }], - 'eol-last': ['error', 'always'], - 'semi': ['error', 'always'], - 'quotes': ['error', 'single'], - 'comma-dangle': ['error', 'always-multiline'], - 'keyword-spacing': ['error', { - 'before': true, - 'after': true, - }], - 'key-spacing': ['error', { - 'beforeColon': false, - 'afterColon': true, - }], - 'space-infix-ops': ['error'], - 'space-before-blocks': ['error', 'always'], - 'object-curly-spacing': ['error', 'always'], - 'nonblock-statement-body-position': ['error', 'beside'], - 'eqeqeq': ['error', 'always', { 'null': 'ignore' }], - 'no-multiple-empty-lines': ['error', { 'max': 1 }], - 'no-multi-spaces': ['error'], - 'no-var': ['error'], - 'prefer-arrow-callback': ['error'], - 'no-throw-literal': ['error'], - 'no-param-reassign': ['warn'], - 'no-constant-condition': ['warn'], - 'no-empty-pattern': ['warn'], - '@typescript-eslint/no-unnecessary-condition': ['error'], - '@typescript-eslint/no-inferrable-types': ['warn'], - '@typescript-eslint/no-non-null-assertion': ['warn'], - '@typescript-eslint/explicit-function-return-type': ['warn'], - '@typescript-eslint/no-misused-promises': ['error', { - 'checksVoidReturn': false, - }], - '@typescript-eslint/consistent-type-imports': 'error', + indent: [ + "error", + "tab", + { + SwitchCase: 1, + MemberExpression: "off", + flatTernaryExpressions: true, + ArrayExpression: "first", + ObjectExpression: "first", + }, + ], + "eol-last": ["error", "always"], + semi: ["error", "always"], + quotes: ["error", "single"], + "comma-dangle": ["error", "always-multiline"], + "keyword-spacing": [ + "error", + { + before: true, + after: true, + }, + ], + "key-spacing": [ + "error", + { + beforeColon: false, + afterColon: true, + }, + ], + "space-infix-ops": ["error"], + "space-before-blocks": ["error", "always"], + "object-curly-spacing": ["error", "always"], + "nonblock-statement-body-position": ["error", "beside"], + eqeqeq: ["error", "always", { null: "ignore" }], + "no-multiple-empty-lines": ["error", { max: 1 }], + "no-multi-spaces": ["error"], + "no-var": ["error"], + "prefer-arrow-callback": ["error"], + "no-throw-literal": ["error"], + "no-param-reassign": ["warn"], + "no-constant-condition": ["warn"], + "no-empty-pattern": ["warn"], + "@typescript-eslint/no-unnecessary-condition": ["error"], + "@typescript-eslint/no-inferrable-types": ["warn"], + "@typescript-eslint/no-non-null-assertion": ["warn"], + "@typescript-eslint/explicit-function-return-type": ["warn"], + "@typescript-eslint/no-misused-promises": [ + "error", + { + checksVoidReturn: false, + }, + ], + "@typescript-eslint/consistent-type-imports": "error", }, }; diff --git a/packages/calckey-js/jest.config.ts b/packages/calckey-js/jest.config.ts index 6d7eeddfea..f26a77c278 100644 --- a/packages/calckey-js/jest.config.ts +++ b/packages/calckey-js/jest.config.ts @@ -1,7 +1,7 @@ /* -* For a detailed explanation regarding each configuration property and type check, visit: -* https://jestjs.io/docs/en/configuration.html -*/ + * For a detailed explanation regarding each configuration property and type check, visit: + * https://jestjs.io/docs/en/configuration.html + */ export default { // All imported modules in your tests should be mocked automatically @@ -117,9 +117,7 @@ export default { // rootDir: undefined, // A list of paths to directories that Jest should use to search for files in - roots: [ - "" - ], + roots: [""], // Allows you to use a custom runner instead of Jest's default test runner // runner: "jest-runner", @@ -149,7 +147,7 @@ export default { testMatch: [ "**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[tj]s?(x)", - "/test/**/*" + "/test/**/*", ], // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped @@ -174,7 +172,7 @@ export default { // A map from regular expressions to paths to transformers transform: { - "^.+\\.(ts|tsx)$": "ts-jest" + "^.+\\.(ts|tsx)$": "ts-jest", }, // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation diff --git a/packages/calckey-js/src/acct.ts b/packages/calckey-js/src/acct.ts index c32cee86c9..5b7767a106 100644 --- a/packages/calckey-js/src/acct.ts +++ b/packages/calckey-js/src/acct.ts @@ -4,8 +4,8 @@ export type Acct = { }; export function parse(acct: string): Acct { - if (acct.startsWith('@')) acct = acct.substr(1); - const split = acct.split('@', 2); + if (acct.startsWith("@")) acct = acct.substr(1); + const split = acct.split("@", 2); return { username: split[0], host: split[1] || null }; } diff --git a/packages/calckey-js/src/api.ts b/packages/calckey-js/src/api.ts index 2cf8488106..141aa4b858 100644 --- a/packages/calckey-js/src/api.ts +++ b/packages/calckey-js/src/api.ts @@ -1,4 +1,4 @@ -import { Endpoints } from './api.types'; +import { Endpoints } from "./api.types"; const MK_API_ERROR = Symbol(); @@ -6,7 +6,7 @@ export type APIError = { id: string; code: string; message: string; - kind: 'client' | 'server'; + kind: "client" | "server"; info: Record; }; @@ -14,25 +14,38 @@ export function isAPIError(reason: any): reason is APIError { return reason[MK_API_ERROR] === true; } -export type FetchLike = (input: string, init?: { +export type FetchLike = ( + input: string, + init?: { method?: string; body?: string; credentials?: RequestCredentials; cache?: RequestCache; - }) => Promise<{ - status: number; - json(): Promise; - }>; + }, +) => Promise<{ + status: number; + json(): Promise; +}>; type IsNeverType = [T] extends [never] ? true : false; type StrictExtract = Cond extends Union ? Union : never; -type IsCaseMatched = - IsNeverType> extends false ? true : false; +type IsCaseMatched< + E extends keyof Endpoints, + P extends Endpoints[E]["req"], + C extends number, +> = IsNeverType< + StrictExtract +> extends false + ? true + : false; -type GetCaseResult = - StrictExtract[1]; +type GetCaseResult< + E extends keyof Endpoints, + P extends Endpoints[E]["req"], + C extends number, +> = StrictExtract[1]; export class APIClient { public origin: string; @@ -40,9 +53,9 @@ export class APIClient { public fetch: FetchLike; constructor(opts: { - origin: APIClient['origin']; - credential?: APIClient['credential']; - fetch?: APIClient['fetch'] | null | undefined; + origin: APIClient["origin"]; + credential?: APIClient["credential"]; + fetch?: APIClient["fetch"] | null | undefined; }) { this.origin = opts.origin; this.credential = opts.credential; @@ -51,48 +64,64 @@ export class APIClient { this.fetch = opts.fetch || ((...args) => fetch(...args)); } - public request( - endpoint: E, params: P = {} as P, credential?: string | null | undefined, - ): Promise extends true ? GetCaseResult : - IsCaseMatched extends true ? GetCaseResult : - IsCaseMatched extends true ? GetCaseResult : - IsCaseMatched extends true ? GetCaseResult : - IsCaseMatched extends true ? GetCaseResult : - IsCaseMatched extends true ? GetCaseResult : - IsCaseMatched extends true ? GetCaseResult : - IsCaseMatched extends true ? GetCaseResult : - IsCaseMatched extends true ? GetCaseResult : - IsCaseMatched extends true ? GetCaseResult : - Endpoints[E]['res']['$switch']['$default'] - : Endpoints[E]['res']> - { + public request( + endpoint: E, + params: P = {} as P, + credential?: string | null | undefined, + ): Promise< + Endpoints[E]["res"] extends { + $switch: { $cases: [any, any][]; $default: any }; + } + ? IsCaseMatched extends true + ? GetCaseResult + : IsCaseMatched extends true + ? GetCaseResult + : IsCaseMatched extends true + ? GetCaseResult + : IsCaseMatched extends true + ? GetCaseResult + : IsCaseMatched extends true + ? GetCaseResult + : IsCaseMatched extends true + ? GetCaseResult + : IsCaseMatched extends true + ? GetCaseResult + : IsCaseMatched extends true + ? GetCaseResult + : IsCaseMatched extends true + ? GetCaseResult + : IsCaseMatched extends true + ? GetCaseResult + : Endpoints[E]["res"]["$switch"]["$default"] + : Endpoints[E]["res"] + > { const promise = new Promise((resolve, reject) => { this.fetch(`${this.origin}/api/${endpoint}`, { - method: 'POST', + method: "POST", body: JSON.stringify({ ...params, i: credential !== undefined ? credential : this.credential, }), - credentials: 'omit', - cache: 'no-cache', - }).then(async (res) => { - const body = res.status === 204 ? null : await res.json(); - - if (res.status === 200) { - resolve(body); - } else if (res.status === 204) { - resolve(null); - } else { - reject({ - [MK_API_ERROR]: true, - ...body.error, - }); - } - }).catch(reject); + credentials: "omit", + cache: "no-cache", + }) + .then(async (res) => { + const body = res.status === 204 ? null : await res.json(); + + if (res.status === 200) { + resolve(body); + } else if (res.status === 204) { + resolve(null); + } else { + reject({ + [MK_API_ERROR]: true, + ...body.error, + }); + } + }) + .catch(reject); }); - + return promise as any; } } diff --git a/packages/calckey-js/src/api.types.ts b/packages/calckey-js/src/api.types.ts index 86aabaca48..bbe781e92a 100644 --- a/packages/calckey-js/src/api.types.ts +++ b/packages/calckey-js/src/api.types.ts @@ -1,134 +1,320 @@ import { - Ad, Announcement, Antenna, App, AuthSession, Blocking, Channel, Clip, DateString, DetailedInstanceMetadata, DriveFile, DriveFolder, Following, FollowingFolloweePopulated, FollowingFollowerPopulated, FollowRequest, GalleryPost, Instance, InstanceMetadata, + Ad, + Announcement, + Antenna, + App, + AuthSession, + Blocking, + Channel, + Clip, + DateString, + DetailedInstanceMetadata, + DriveFile, + DriveFolder, + Following, + FollowingFolloweePopulated, + FollowingFollowerPopulated, + FollowRequest, + GalleryPost, + Instance, + InstanceMetadata, LiteInstanceMetadata, MeDetailed, - Note, NoteFavorite, OriginType, Page, ServerInfo, Stats, User, UserDetailed, UserGroup, UserList, UserSorting, Notification, NoteReaction, Signin, MessagingMessage, -} from './entities'; + Note, + NoteFavorite, + OriginType, + Page, + ServerInfo, + Stats, + User, + UserDetailed, + UserGroup, + UserList, + UserSorting, + Notification, + NoteReaction, + Signin, + MessagingMessage, +} from "./entities"; type TODO = Record | null; type NoParams = Record; -type ShowUserReq = { username: string; host?: string; } | { userId: User['id']; }; +type ShowUserReq = { username: string; host?: string } | { userId: User["id"] }; export type Endpoints = { // admin - 'admin/abuse-user-reports': { req: TODO; res: TODO; }; - 'admin/delete-all-files-of-a-user': { req: { userId: User['id']; }; res: null; }; - 'admin/delete-logs': { req: NoParams; res: null; }; - 'admin/get-index-stats': { req: TODO; res: TODO; }; - 'admin/get-table-stats': { req: TODO; res: TODO; }; - 'admin/invite': { req: TODO; res: TODO; }; - 'admin/logs': { req: TODO; res: TODO; }; - 'admin/reset-password': { req: TODO; res: TODO; }; - 'admin/resolve-abuse-user-report': { req: TODO; res: TODO; }; - 'admin/resync-chart': { req: TODO; res: TODO; }; - 'admin/send-email': { req: TODO; res: TODO; }; - 'admin/server-info': { req: TODO; res: TODO; }; - 'admin/show-moderation-logs': { req: TODO; res: TODO; }; - 'admin/show-user': { req: TODO; res: TODO; }; - 'admin/show-users': { req: TODO; res: TODO; }; - 'admin/silence-user': { req: TODO; res: TODO; }; - 'admin/suspend-user': { req: TODO; res: TODO; }; - 'admin/unsilence-user': { req: TODO; res: TODO; }; - 'admin/unsuspend-user': { req: TODO; res: TODO; }; - 'admin/update-meta': { req: TODO; res: TODO; }; - 'admin/vacuum': { req: TODO; res: TODO; }; - 'admin/accounts/create': { req: TODO; res: TODO; }; - 'admin/ad/create': { req: TODO; res: TODO; }; - 'admin/ad/delete': { req: { id: Ad['id']; }; res: null; }; - 'admin/ad/list': { req: TODO; res: TODO; }; - 'admin/ad/update': { req: TODO; res: TODO; }; - 'admin/announcements/create': { req: TODO; res: TODO; }; - 'admin/announcements/delete': { req: { id: Announcement['id'] }; res: null; }; - 'admin/announcements/list': { req: TODO; res: TODO; }; - 'admin/announcements/update': { req: TODO; res: TODO; }; - 'admin/drive/clean-remote-files': { req: TODO; res: TODO; }; - 'admin/drive/cleanup': { req: TODO; res: TODO; }; - 'admin/drive/files': { req: TODO; res: TODO; }; - 'admin/drive/show-file': { req: TODO; res: TODO; }; - 'admin/emoji/add': { req: TODO; res: TODO; }; - 'admin/emoji/copy': { req: TODO; res: TODO; }; - 'admin/emoji/list-remote': { req: TODO; res: TODO; }; - 'admin/emoji/list': { req: TODO; res: TODO; }; - 'admin/emoji/remove': { req: TODO; res: TODO; }; - 'admin/emoji/update': { req: TODO; res: TODO; }; - 'admin/federation/delete-all-files': { req: { host: string; }; res: null; }; - 'admin/federation/refresh-remote-instance-metadata': { req: TODO; res: TODO; }; - 'admin/federation/remove-all-following': { req: TODO; res: TODO; }; - 'admin/federation/update-instance': { req: TODO; res: TODO; }; - 'admin/moderators/add': { req: TODO; res: TODO; }; - 'admin/moderators/remove': { req: TODO; res: TODO; }; - 'admin/promo/create': { req: TODO; res: TODO; }; - 'admin/queue/clear': { req: TODO; res: TODO; }; - 'admin/queue/deliver-delayed': { req: TODO; res: TODO; }; - 'admin/queue/inbox-delayed': { req: TODO; res: TODO; }; - 'admin/queue/jobs': { req: TODO; res: TODO; }; - 'admin/queue/stats': { req: TODO; res: TODO; }; - 'admin/relays/add': { req: TODO; res: TODO; }; - 'admin/relays/list': { req: TODO; res: TODO; }; - 'admin/relays/remove': { req: TODO; res: TODO; }; + "admin/abuse-user-reports": { req: TODO; res: TODO }; + "admin/delete-all-files-of-a-user": { + req: { userId: User["id"] }; + res: null; + }; + "admin/delete-logs": { req: NoParams; res: null }; + "admin/get-index-stats": { req: TODO; res: TODO }; + "admin/get-table-stats": { req: TODO; res: TODO }; + "admin/invite": { req: TODO; res: TODO }; + "admin/logs": { req: TODO; res: TODO }; + "admin/reset-password": { req: TODO; res: TODO }; + "admin/resolve-abuse-user-report": { req: TODO; res: TODO }; + "admin/resync-chart": { req: TODO; res: TODO }; + "admin/send-email": { req: TODO; res: TODO }; + "admin/server-info": { req: TODO; res: TODO }; + "admin/show-moderation-logs": { req: TODO; res: TODO }; + "admin/show-user": { req: TODO; res: TODO }; + "admin/show-users": { req: TODO; res: TODO }; + "admin/silence-user": { req: TODO; res: TODO }; + "admin/suspend-user": { req: TODO; res: TODO }; + "admin/unsilence-user": { req: TODO; res: TODO }; + "admin/unsuspend-user": { req: TODO; res: TODO }; + "admin/update-meta": { req: TODO; res: TODO }; + "admin/vacuum": { req: TODO; res: TODO }; + "admin/accounts/create": { req: TODO; res: TODO }; + "admin/ad/create": { req: TODO; res: TODO }; + "admin/ad/delete": { req: { id: Ad["id"] }; res: null }; + "admin/ad/list": { req: TODO; res: TODO }; + "admin/ad/update": { req: TODO; res: TODO }; + "admin/announcements/create": { req: TODO; res: TODO }; + "admin/announcements/delete": { req: { id: Announcement["id"] }; res: null }; + "admin/announcements/list": { req: TODO; res: TODO }; + "admin/announcements/update": { req: TODO; res: TODO }; + "admin/drive/clean-remote-files": { req: TODO; res: TODO }; + "admin/drive/cleanup": { req: TODO; res: TODO }; + "admin/drive/files": { req: TODO; res: TODO }; + "admin/drive/show-file": { req: TODO; res: TODO }; + "admin/emoji/add": { req: TODO; res: TODO }; + "admin/emoji/copy": { req: TODO; res: TODO }; + "admin/emoji/list-remote": { req: TODO; res: TODO }; + "admin/emoji/list": { req: TODO; res: TODO }; + "admin/emoji/remove": { req: TODO; res: TODO }; + "admin/emoji/update": { req: TODO; res: TODO }; + "admin/federation/delete-all-files": { req: { host: string }; res: null }; + "admin/federation/refresh-remote-instance-metadata": { req: TODO; res: TODO }; + "admin/federation/remove-all-following": { req: TODO; res: TODO }; + "admin/federation/update-instance": { req: TODO; res: TODO }; + "admin/moderators/add": { req: TODO; res: TODO }; + "admin/moderators/remove": { req: TODO; res: TODO }; + "admin/promo/create": { req: TODO; res: TODO }; + "admin/queue/clear": { req: TODO; res: TODO }; + "admin/queue/deliver-delayed": { req: TODO; res: TODO }; + "admin/queue/inbox-delayed": { req: TODO; res: TODO }; + "admin/queue/jobs": { req: TODO; res: TODO }; + "admin/queue/stats": { req: TODO; res: TODO }; + "admin/relays/add": { req: TODO; res: TODO }; + "admin/relays/list": { req: TODO; res: TODO }; + "admin/relays/remove": { req: TODO; res: TODO }; // announcements - 'announcements': { req: { limit?: number; withUnreads?: boolean; sinceId?: Announcement['id']; untilId?: Announcement['id']; }; res: Announcement[]; }; + announcements: { + req: { + limit?: number; + withUnreads?: boolean; + sinceId?: Announcement["id"]; + untilId?: Announcement["id"]; + }; + res: Announcement[]; + }; // antennas - 'antennas/create': { req: TODO; res: Antenna; }; - 'antennas/delete': { req: { antennaId: Antenna['id']; }; res: null; }; - 'antennas/list': { req: NoParams; res: Antenna[]; }; - 'antennas/notes': { req: { antennaId: Antenna['id']; limit?: number; sinceId?: Note['id']; untilId?: Note['id']; }; res: Note[]; }; - 'antennas/show': { req: { antennaId: Antenna['id']; }; res: Antenna; }; - 'antennas/update': { req: TODO; res: Antenna; }; - 'antennas/mark-read': { req: TODO; res: Antenna; }; + "antennas/create": { req: TODO; res: Antenna }; + "antennas/delete": { req: { antennaId: Antenna["id"] }; res: null }; + "antennas/list": { req: NoParams; res: Antenna[] }; + "antennas/notes": { + req: { + antennaId: Antenna["id"]; + limit?: number; + sinceId?: Note["id"]; + untilId?: Note["id"]; + }; + res: Note[]; + }; + "antennas/show": { req: { antennaId: Antenna["id"] }; res: Antenna }; + "antennas/update": { req: TODO; res: Antenna }; + "antennas/mark-read": { req: TODO; res: Antenna }; // ap - 'ap/get': { req: { uri: string; }; res: Record; }; - 'ap/show': { req: { uri: string; }; res: { - type: 'Note'; - object: Note; - } | { - type: 'User'; - object: UserDetailed; - }; }; + "ap/get": { req: { uri: string }; res: Record }; + "ap/show": { + req: { uri: string }; + res: + | { + type: "Note"; + object: Note; + } + | { + type: "User"; + object: UserDetailed; + }; + }; // app - 'app/create': { req: TODO; res: App; }; - 'app/show': { req: { appId: App['id']; }; res: App; }; + "app/create": { req: TODO; res: App }; + "app/show": { req: { appId: App["id"] }; res: App }; // auth - 'auth/accept': { req: { token: string; }; res: null; }; - 'auth/session/generate': { req: { appSecret: string; }; res: { token: string; url: string; }; }; - 'auth/session/show': { req: { token: string; }; res: AuthSession; }; - 'auth/session/userkey': { req: { appSecret: string; token: string; }; res: { accessToken: string; user: User }; }; + "auth/accept": { req: { token: string }; res: null }; + "auth/session/generate": { + req: { appSecret: string }; + res: { token: string; url: string }; + }; + "auth/session/show": { req: { token: string }; res: AuthSession }; + "auth/session/userkey": { + req: { appSecret: string; token: string }; + res: { accessToken: string; user: User }; + }; // blocking - 'blocking/create': { req: { userId: User['id'] }; res: UserDetailed; }; - 'blocking/delete': { req: { userId: User['id'] }; res: UserDetailed; }; - 'blocking/list': { req: { limit?: number; sinceId?: Blocking['id']; untilId?: Blocking['id']; }; res: Blocking[]; }; + "blocking/create": { req: { userId: User["id"] }; res: UserDetailed }; + "blocking/delete": { req: { userId: User["id"] }; res: UserDetailed }; + "blocking/list": { + req: { limit?: number; sinceId?: Blocking["id"]; untilId?: Blocking["id"] }; + res: Blocking[]; + }; // channels - 'channels/create': { req: TODO; res: TODO; }; - 'channels/featured': { req: TODO; res: TODO; }; - 'channels/follow': { req: TODO; res: TODO; }; - 'channels/followed': { req: TODO; res: TODO; }; - 'channels/owned': { req: TODO; res: TODO; }; - 'channels/pin-note': { req: TODO; res: TODO; }; - 'channels/show': { req: TODO; res: TODO; }; - 'channels/timeline': { req: TODO; res: TODO; }; - 'channels/unfollow': { req: TODO; res: TODO; }; - 'channels/update': { req: TODO; res: TODO; }; + "channels/create": { req: TODO; res: TODO }; + "channels/featured": { req: TODO; res: TODO }; + "channels/follow": { req: TODO; res: TODO }; + "channels/followed": { req: TODO; res: TODO }; + "channels/owned": { req: TODO; res: TODO }; + "channels/pin-note": { req: TODO; res: TODO }; + "channels/show": { req: TODO; res: TODO }; + "channels/timeline": { req: TODO; res: TODO }; + "channels/unfollow": { req: TODO; res: TODO }; + "channels/update": { req: TODO; res: TODO }; // charts - 'charts/active-users': { req: { span: 'day' | 'hour'; limit?: number; offset?: number | null; }; res: { - local: { - users: number[]; + "charts/active-users": { + req: { span: "day" | "hour"; limit?: number; offset?: number | null }; + res: { + local: { + users: number[]; + }; + remote: { + users: number[]; + }; }; - remote: { - users: number[]; + }; + "charts/drive": { + req: { span: "day" | "hour"; limit?: number; offset?: number | null }; + res: { + local: { + decCount: number[]; + decSize: number[]; + incCount: number[]; + incSize: number[]; + totalCount: number[]; + totalSize: number[]; + }; + remote: { + decCount: number[]; + decSize: number[]; + incCount: number[]; + incSize: number[]; + totalCount: number[]; + totalSize: number[]; + }; }; - }; }; - 'charts/drive': { req: { span: 'day' | 'hour'; limit?: number; offset?: number | null; }; res: { - local: { + }; + "charts/federation": { + req: { span: "day" | "hour"; limit?: number; offset?: number | null }; + res: { + instance: { + dec: number[]; + inc: number[]; + total: number[]; + }; + }; + }; + "charts/hashtag": { + req: { span: "day" | "hour"; limit?: number; offset?: number | null }; + res: TODO; + }; + "charts/instance": { + req: { + span: "day" | "hour"; + limit?: number; + offset?: number | null; + host: string; + }; + res: { + drive: { + decFiles: number[]; + decUsage: number[]; + incFiles: number[]; + incUsage: number[]; + totalFiles: number[]; + totalUsage: number[]; + }; + followers: { + dec: number[]; + inc: number[]; + total: number[]; + }; + following: { + dec: number[]; + inc: number[]; + total: number[]; + }; + notes: { + dec: number[]; + inc: number[]; + total: number[]; + diffs: { + normal: number[]; + renote: number[]; + reply: number[]; + }; + }; + requests: { + failed: number[]; + received: number[]; + succeeded: number[]; + }; + users: { + dec: number[]; + inc: number[]; + total: number[]; + }; + }; + }; + "charts/network": { + req: { span: "day" | "hour"; limit?: number; offset?: number | null }; + res: TODO; + }; + "charts/notes": { + req: { span: "day" | "hour"; limit?: number; offset?: number | null }; + res: { + local: { + dec: number[]; + inc: number[]; + total: number[]; + diffs: { + normal: number[]; + renote: number[]; + reply: number[]; + }; + }; + remote: { + dec: number[]; + inc: number[]; + total: number[]; + diffs: { + normal: number[]; + renote: number[]; + reply: number[]; + }; + }; + }; + }; + "charts/user/drive": { + req: { + span: "day" | "hour"; + limit?: number; + offset?: number | null; + userId: User["id"]; + }; + res: { decCount: number[]; decSize: number[]; incCount: number[]; @@ -136,43 +322,24 @@ export type Endpoints = { totalCount: number[]; totalSize: number[]; }; - remote: { - decCount: number[]; - decSize: number[]; - incCount: number[]; - incSize: number[]; - totalCount: number[]; - totalSize: number[]; + }; + "charts/user/following": { + req: { + span: "day" | "hour"; + limit?: number; + offset?: number | null; + userId: User["id"]; }; - }; }; - 'charts/federation': { req: { span: 'day' | 'hour'; limit?: number; offset?: number | null; }; res: { - instance: { - dec: number[]; - inc: number[]; - total: number[]; + res: TODO; + }; + "charts/user/notes": { + req: { + span: "day" | "hour"; + limit?: number; + offset?: number | null; + userId: User["id"]; }; - }; }; - 'charts/hashtag': { req: { span: 'day' | 'hour'; limit?: number; offset?: number | null; }; res: TODO; }; - 'charts/instance': { req: { span: 'day' | 'hour'; limit?: number; offset?: number | null; host: string; }; res: { - drive: { - decFiles: number[]; - decUsage: number[]; - incFiles: number[]; - incUsage: number[]; - totalFiles: number[]; - totalUsage: number[]; - }; - followers: { - dec: number[]; - inc: number[]; - total: number[]; - }; - following: { - dec: number[]; - inc: number[]; - total: number[]; - }; - notes: { + res: { dec: number[]; inc: number[]; total: number[]; @@ -182,431 +349,733 @@ export type Endpoints = { reply: number[]; }; }; - requests: { - failed: number[]; - received: number[]; - succeeded: number[]; + }; + "charts/user/reactions": { + req: { + span: "day" | "hour"; + limit?: number; + offset?: number | null; + userId: User["id"]; }; - users: { - dec: number[]; - inc: number[]; - total: number[]; - }; - }; }; - 'charts/network': { req: { span: 'day' | 'hour'; limit?: number; offset?: number | null; }; res: TODO; }; - 'charts/notes': { req: { span: 'day' | 'hour'; limit?: number; offset?: number | null; }; res: { - local: { - dec: number[]; - inc: number[]; - total: number[]; - diffs: { - normal: number[]; - renote: number[]; - reply: number[]; + res: TODO; + }; + "charts/users": { + req: { span: "day" | "hour"; limit?: number; offset?: number | null }; + res: { + local: { + dec: number[]; + inc: number[]; + total: number[]; + }; + remote: { + dec: number[]; + inc: number[]; + total: number[]; }; }; - remote: { - dec: number[]; - inc: number[]; - total: number[]; - diffs: { - normal: number[]; - renote: number[]; - reply: number[]; - }; - }; - }; }; - 'charts/user/drive': { req: { span: 'day' | 'hour'; limit?: number; offset?: number | null; userId: User['id']; }; res: { - decCount: number[]; - decSize: number[]; - incCount: number[]; - incSize: number[]; - totalCount: number[]; - totalSize: number[]; - }; }; - 'charts/user/following': { req: { span: 'day' | 'hour'; limit?: number; offset?: number | null; userId: User['id']; }; res: TODO; }; - 'charts/user/notes': { req: { span: 'day' | 'hour'; limit?: number; offset?: number | null; userId: User['id']; }; res: { - dec: number[]; - inc: number[]; - total: number[]; - diffs: { - normal: number[]; - renote: number[]; - reply: number[]; - }; - }; }; - 'charts/user/reactions': { req: { span: 'day' | 'hour'; limit?: number; offset?: number | null; userId: User['id']; }; res: TODO; }; - 'charts/users': { req: { span: 'day' | 'hour'; limit?: number; offset?: number | null; }; res: { - local: { - dec: number[]; - inc: number[]; - total: number[]; - }; - remote: { - dec: number[]; - inc: number[]; - total: number[]; - }; - }; }; + }; // clips - 'clips/add-note': { req: TODO; res: TODO; }; - 'clips/create': { req: TODO; res: TODO; }; - 'clips/delete': { req: { clipId: Clip['id']; }; res: null; }; - 'clips/list': { req: TODO; res: TODO; }; - 'clips/notes': { req: TODO; res: TODO; }; - 'clips/show': { req: TODO; res: TODO; }; - 'clips/update': { req: TODO; res: TODO; }; + "clips/add-note": { req: TODO; res: TODO }; + "clips/create": { req: TODO; res: TODO }; + "clips/delete": { req: { clipId: Clip["id"] }; res: null }; + "clips/list": { req: TODO; res: TODO }; + "clips/notes": { req: TODO; res: TODO }; + "clips/show": { req: TODO; res: TODO }; + "clips/update": { req: TODO; res: TODO }; // drive - 'drive': { req: NoParams; res: { capacity: number; usage: number; }; }; - 'drive/files': { req: { folderId?: DriveFolder['id'] | null; type?: DriveFile['type'] | null; limit?: number; sinceId?: DriveFile['id']; untilId?: DriveFile['id']; }; res: DriveFile[]; }; - 'drive/files/attached-notes': { req: TODO; res: TODO; }; - 'drive/files/check-existence': { req: TODO; res: TODO; }; - 'drive/files/create': { req: TODO; res: TODO; }; - 'drive/files/delete': { req: { fileId: DriveFile['id']; }; res: null; }; - 'drive/files/find-by-hash': { req: TODO; res: TODO; }; - 'drive/files/find': { req: { name: string; folderId?: DriveFolder['id'] | null; }; res: DriveFile[]; }; - 'drive/files/show': { req: { fileId?: DriveFile['id']; url?: string; }; res: DriveFile; }; - 'drive/files/update': { req: { fileId: DriveFile['id']; folderId?: DriveFolder['id'] | null; name?: string; isSensitive?: boolean; comment?: string | null; }; res: DriveFile; }; - 'drive/files/upload-from-url': { req: { url: string; folderId?: DriveFolder['id'] | null; isSensitive?: boolean; comment?: string | null; marker?: string | null; force?: boolean; }; res: null; }; - 'drive/folders': { req: { folderId?: DriveFolder['id'] | null; limit?: number; sinceId?: DriveFile['id']; untilId?: DriveFile['id']; }; res: DriveFolder[]; }; - 'drive/folders/create': { req: { name?: string; parentId?: DriveFolder['id'] | null; }; res: DriveFolder; }; - 'drive/folders/delete': { req: { folderId: DriveFolder['id']; }; res: null; }; - 'drive/folders/find': { req: { name: string; parentId?: DriveFolder['id'] | null; }; res: DriveFolder[]; }; - 'drive/folders/show': { req: { folderId: DriveFolder['id']; }; res: DriveFolder; }; - 'drive/folders/update': { req: { folderId: DriveFolder['id']; name?: string; parentId?: DriveFolder['id'] | null; }; res: DriveFolder; }; - 'drive/stream': { req: { type?: DriveFile['type'] | null; limit?: number; sinceId?: DriveFile['id']; untilId?: DriveFile['id']; }; res: DriveFile[]; }; + drive: { req: NoParams; res: { capacity: number; usage: number } }; + "drive/files": { + req: { + folderId?: DriveFolder["id"] | null; + type?: DriveFile["type"] | null; + limit?: number; + sinceId?: DriveFile["id"]; + untilId?: DriveFile["id"]; + }; + res: DriveFile[]; + }; + "drive/files/attached-notes": { req: TODO; res: TODO }; + "drive/files/check-existence": { req: TODO; res: TODO }; + "drive/files/create": { req: TODO; res: TODO }; + "drive/files/delete": { req: { fileId: DriveFile["id"] }; res: null }; + "drive/files/find-by-hash": { req: TODO; res: TODO }; + "drive/files/find": { + req: { name: string; folderId?: DriveFolder["id"] | null }; + res: DriveFile[]; + }; + "drive/files/show": { + req: { fileId?: DriveFile["id"]; url?: string }; + res: DriveFile; + }; + "drive/files/update": { + req: { + fileId: DriveFile["id"]; + folderId?: DriveFolder["id"] | null; + name?: string; + isSensitive?: boolean; + comment?: string | null; + }; + res: DriveFile; + }; + "drive/files/upload-from-url": { + req: { + url: string; + folderId?: DriveFolder["id"] | null; + isSensitive?: boolean; + comment?: string | null; + marker?: string | null; + force?: boolean; + }; + res: null; + }; + "drive/folders": { + req: { + folderId?: DriveFolder["id"] | null; + limit?: number; + sinceId?: DriveFile["id"]; + untilId?: DriveFile["id"]; + }; + res: DriveFolder[]; + }; + "drive/folders/create": { + req: { name?: string; parentId?: DriveFolder["id"] | null }; + res: DriveFolder; + }; + "drive/folders/delete": { req: { folderId: DriveFolder["id"] }; res: null }; + "drive/folders/find": { + req: { name: string; parentId?: DriveFolder["id"] | null }; + res: DriveFolder[]; + }; + "drive/folders/show": { + req: { folderId: DriveFolder["id"] }; + res: DriveFolder; + }; + "drive/folders/update": { + req: { + folderId: DriveFolder["id"]; + name?: string; + parentId?: DriveFolder["id"] | null; + }; + res: DriveFolder; + }; + "drive/stream": { + req: { + type?: DriveFile["type"] | null; + limit?: number; + sinceId?: DriveFile["id"]; + untilId?: DriveFile["id"]; + }; + res: DriveFile[]; + }; // endpoint - 'endpoint': { req: { endpoint: string; }; res: { params: { name: string; type: string; }[]; }; }; + endpoint: { + req: { endpoint: string }; + res: { params: { name: string; type: string }[] }; + }; // endpoints - 'endpoints': { req: NoParams; res: string[]; }; + endpoints: { req: NoParams; res: string[] }; // federation - 'federation/dns': { req: { host: string; }; res: { - a: string[]; - aaaa: string[]; - cname: string[]; - txt: string[]; - }; }; - 'federation/followers': { req: { host: string; limit?: number; sinceId?: Following['id']; untilId?: Following['id']; }; res: FollowingFolloweePopulated[]; }; - 'federation/following': { req: { host: string; limit?: number; sinceId?: Following['id']; untilId?: Following['id']; }; res: FollowingFolloweePopulated[]; }; - 'federation/instances': { req: { - host?: string | null; - blocked?: boolean | null; - notResponding?: boolean | null; - suspended?: boolean | null; - federating?: boolean | null; - subscribing?: boolean | null; - publishing?: boolean | null; - limit?: number; - offset?: number; - sort?: '+pubSub' | '-pubSub' | '+notes' | '-notes' | '+users' | '-users' | '+following' | '-following' | '+followers' | '-followers' | '+caughtAt' | '-caughtAt' | '+lastCommunicatedAt' | '-lastCommunicatedAt' | '+driveUsage' | '-driveUsage' | '+driveFiles' | '-driveFiles'; - }; res: Instance[]; }; - 'federation/show-instance': { req: { host: string; }; res: Instance; }; - 'federation/update-remote-user': { req: { userId: User['id']; }; res: null; }; - 'federation/users': { req: { host: string; limit?: number; sinceId?: User['id']; untilId?: User['id']; }; res: UserDetailed[]; }; + "federation/dns": { + req: { host: string }; + res: { + a: string[]; + aaaa: string[]; + cname: string[]; + txt: string[]; + }; + }; + "federation/followers": { + req: { + host: string; + limit?: number; + sinceId?: Following["id"]; + untilId?: Following["id"]; + }; + res: FollowingFolloweePopulated[]; + }; + "federation/following": { + req: { + host: string; + limit?: number; + sinceId?: Following["id"]; + untilId?: Following["id"]; + }; + res: FollowingFolloweePopulated[]; + }; + "federation/instances": { + req: { + host?: string | null; + blocked?: boolean | null; + notResponding?: boolean | null; + suspended?: boolean | null; + federating?: boolean | null; + subscribing?: boolean | null; + publishing?: boolean | null; + limit?: number; + offset?: number; + sort?: + | "+pubSub" + | "-pubSub" + | "+notes" + | "-notes" + | "+users" + | "-users" + | "+following" + | "-following" + | "+followers" + | "-followers" + | "+caughtAt" + | "-caughtAt" + | "+lastCommunicatedAt" + | "-lastCommunicatedAt" + | "+driveUsage" + | "-driveUsage" + | "+driveFiles" + | "-driveFiles"; + }; + res: Instance[]; + }; + "federation/show-instance": { req: { host: string }; res: Instance }; + "federation/update-remote-user": { req: { userId: User["id"] }; res: null }; + "federation/users": { + req: { + host: string; + limit?: number; + sinceId?: User["id"]; + untilId?: User["id"]; + }; + res: UserDetailed[]; + }; // following - 'following/create': { req: { userId: User['id'] }; res: User; }; - 'following/delete': { req: { userId: User['id'] }; res: User; }; - 'following/requests/accept': { req: { userId: User['id'] }; res: null; }; - 'following/requests/cancel': { req: { userId: User['id'] }; res: User; }; - 'following/requests/list': { req: NoParams; res: FollowRequest[]; }; - 'following/requests/reject': { req: { userId: User['id'] }; res: null; }; + "following/create": { req: { userId: User["id"] }; res: User }; + "following/delete": { req: { userId: User["id"] }; res: User }; + "following/requests/accept": { req: { userId: User["id"] }; res: null }; + "following/requests/cancel": { req: { userId: User["id"] }; res: User }; + "following/requests/list": { req: NoParams; res: FollowRequest[] }; + "following/requests/reject": { req: { userId: User["id"] }; res: null }; // gallery - 'gallery/featured': { req: TODO; res: TODO; }; - 'gallery/popular': { req: TODO; res: TODO; }; - 'gallery/posts': { req: TODO; res: TODO; }; - 'gallery/posts/create': { req: TODO; res: TODO; }; - 'gallery/posts/delete': { req: { postId: GalleryPost['id'] }; res: null; }; - 'gallery/posts/like': { req: TODO; res: TODO; }; - 'gallery/posts/show': { req: TODO; res: TODO; }; - 'gallery/posts/unlike': { req: TODO; res: TODO; }; - 'gallery/posts/update': { req: TODO; res: TODO; }; + "gallery/featured": { req: TODO; res: TODO }; + "gallery/popular": { req: TODO; res: TODO }; + "gallery/posts": { req: TODO; res: TODO }; + "gallery/posts/create": { req: TODO; res: TODO }; + "gallery/posts/delete": { req: { postId: GalleryPost["id"] }; res: null }; + "gallery/posts/like": { req: TODO; res: TODO }; + "gallery/posts/show": { req: TODO; res: TODO }; + "gallery/posts/unlike": { req: TODO; res: TODO }; + "gallery/posts/update": { req: TODO; res: TODO }; // games - 'games/reversi/games': { req: TODO; res: TODO; }; - 'games/reversi/games/show': { req: TODO; res: TODO; }; - 'games/reversi/games/surrender': { req: TODO; res: TODO; }; - 'games/reversi/invitations': { req: TODO; res: TODO; }; - 'games/reversi/match': { req: TODO; res: TODO; }; - 'games/reversi/match/cancel': { req: TODO; res: TODO; }; + "games/reversi/games": { req: TODO; res: TODO }; + "games/reversi/games/show": { req: TODO; res: TODO }; + "games/reversi/games/surrender": { req: TODO; res: TODO }; + "games/reversi/invitations": { req: TODO; res: TODO }; + "games/reversi/match": { req: TODO; res: TODO }; + "games/reversi/match/cancel": { req: TODO; res: TODO }; // get-online-users-count - 'get-online-users-count': { req: NoParams; res: { count: number; }; }; + "get-online-users-count": { req: NoParams; res: { count: number } }; // hashtags - 'hashtags/list': { req: TODO; res: TODO; }; - 'hashtags/search': { req: TODO; res: TODO; }; - 'hashtags/show': { req: TODO; res: TODO; }; - 'hashtags/trend': { req: TODO; res: TODO; }; - 'hashtags/users': { req: TODO; res: TODO; }; + "hashtags/list": { req: TODO; res: TODO }; + "hashtags/search": { req: TODO; res: TODO }; + "hashtags/show": { req: TODO; res: TODO }; + "hashtags/trend": { req: TODO; res: TODO }; + "hashtags/users": { req: TODO; res: TODO }; // i - 'i': { req: NoParams; res: User; }; - 'i/apps': { req: TODO; res: TODO; }; - 'i/authorized-apps': { req: TODO; res: TODO; }; - 'i/change-password': { req: TODO; res: TODO; }; - 'i/delete-account': { req: { password: string; }; res: null; }; - 'i/export-blocking': { req: TODO; res: TODO; }; - 'i/export-following': { req: TODO; res: TODO; }; - 'i/export-mute': { req: TODO; res: TODO; }; - 'i/export-notes': { req: TODO; res: TODO; }; - 'i/export-user-lists': { req: TODO; res: TODO; }; - 'i/favorites': { req: { limit?: number; sinceId?: NoteFavorite['id']; untilId?: NoteFavorite['id']; }; res: NoteFavorite[]; }; - 'i/gallery/likes': { req: TODO; res: TODO; }; - 'i/gallery/posts': { req: TODO; res: TODO; }; - 'i/get-word-muted-notes-count': { req: TODO; res: TODO; }; - 'i/import-following': { req: TODO; res: TODO; }; - 'i/import-user-lists': { req: TODO; res: TODO; }; - 'i/move': { req: TODO; res: TODO; }; - 'i/known-as': { req: TODO; res: TODO; }; - 'i/notifications': { req: { - limit?: number; - sinceId?: Notification['id']; - untilId?: Notification['id']; - following?: boolean; - markAsRead?: boolean; - includeTypes?: Notification['type'][]; - excludeTypes?: Notification['type'][]; - }; res: Notification[]; }; - 'i/page-likes': { req: TODO; res: TODO; }; - 'i/pages': { req: TODO; res: TODO; }; - 'i/pin': { req: { noteId: Note['id']; }; res: MeDetailed; }; - 'i/read-all-messaging-messages': { req: TODO; res: TODO; }; - 'i/read-all-unread-notes': { req: TODO; res: TODO; }; - 'i/read-announcement': { req: TODO; res: TODO; }; - 'i/regenerate-token': { req: { password: string; }; res: null; }; - 'i/registry/get-all': { req: { scope?: string[]; }; res: Record; }; - 'i/registry/get-detail': { req: { key: string; scope?: string[]; }; res: { updatedAt: DateString; value: any; }; }; - 'i/registry/get': { req: { key: string; scope?: string[]; }; res: any; }; - 'i/registry/keys-with-type': { req: { scope?: string[]; }; res: Record; }; - 'i/registry/keys': { req: { scope?: string[]; }; res: string[]; }; - 'i/registry/remove': { req: { key: string; scope?: string[]; }; res: null; }; - 'i/registry/scopes': { req: NoParams; res: string[][]; }; - 'i/registry/set': { req: { key: string; value: any; scope?: string[]; }; res: null; }; - 'i/revoke-token': { req: TODO; res: TODO; }; - 'i/signin-history': { req: { limit?: number; sinceId?: Signin['id']; untilId?: Signin['id']; }; res: Signin[]; }; - 'i/unpin': { req: { noteId: Note['id']; }; res: MeDetailed; }; - 'i/update-email': { req: { - password: string; - email?: string | null; - }; res: MeDetailed; }; - 'i/update': { req: { - name?: string | null; - description?: string | null; - lang?: string | null; - location?: string | null; - birthday?: string | null; - avatarId?: DriveFile['id'] | null; - bannerId?: DriveFile['id'] | null; - fields?: { - name: string; - value: string; - }[]; - isLocked?: boolean; - isExplorable?: boolean; - hideOnlineStatus?: boolean; - carefulBot?: boolean; - autoAcceptFollowed?: boolean; - noCrawle?: boolean; - isBot?: boolean; - isCat?: boolean; - injectFeaturedNote?: boolean; - receiveAnnouncementEmail?: boolean; - alwaysMarkNsfw?: boolean; - mutedWords?: string[][]; - mutingNotificationTypes?: Notification['type'][]; - emailNotificationTypes?: string[]; - }; res: MeDetailed; }; - 'i/user-group-invites': { req: TODO; res: TODO; }; - 'i/2fa/done': { req: TODO; res: TODO; }; - 'i/2fa/key-done': { req: TODO; res: TODO; }; - 'i/2fa/password-less': { req: TODO; res: TODO; }; - 'i/2fa/register-key': { req: TODO; res: TODO; }; - 'i/2fa/register': { req: TODO; res: TODO; }; - 'i/2fa/remove-key': { req: TODO; res: TODO; }; - 'i/2fa/unregister': { req: TODO; res: TODO; }; + i: { req: NoParams; res: User }; + "i/apps": { req: TODO; res: TODO }; + "i/authorized-apps": { req: TODO; res: TODO }; + "i/change-password": { req: TODO; res: TODO }; + "i/delete-account": { req: { password: string }; res: null }; + "i/export-blocking": { req: TODO; res: TODO }; + "i/export-following": { req: TODO; res: TODO }; + "i/export-mute": { req: TODO; res: TODO }; + "i/export-notes": { req: TODO; res: TODO }; + "i/export-user-lists": { req: TODO; res: TODO }; + "i/favorites": { + req: { + limit?: number; + sinceId?: NoteFavorite["id"]; + untilId?: NoteFavorite["id"]; + }; + res: NoteFavorite[]; + }; + "i/gallery/likes": { req: TODO; res: TODO }; + "i/gallery/posts": { req: TODO; res: TODO }; + "i/get-word-muted-notes-count": { req: TODO; res: TODO }; + "i/import-following": { req: TODO; res: TODO }; + "i/import-user-lists": { req: TODO; res: TODO }; + "i/move": { req: TODO; res: TODO }; + "i/known-as": { req: TODO; res: TODO }; + "i/notifications": { + req: { + limit?: number; + sinceId?: Notification["id"]; + untilId?: Notification["id"]; + following?: boolean; + markAsRead?: boolean; + includeTypes?: Notification["type"][]; + excludeTypes?: Notification["type"][]; + }; + res: Notification[]; + }; + "i/page-likes": { req: TODO; res: TODO }; + "i/pages": { req: TODO; res: TODO }; + "i/pin": { req: { noteId: Note["id"] }; res: MeDetailed }; + "i/read-all-messaging-messages": { req: TODO; res: TODO }; + "i/read-all-unread-notes": { req: TODO; res: TODO }; + "i/read-announcement": { req: TODO; res: TODO }; + "i/regenerate-token": { req: { password: string }; res: null }; + "i/registry/get-all": { req: { scope?: string[] }; res: Record }; + "i/registry/get-detail": { + req: { key: string; scope?: string[] }; + res: { updatedAt: DateString; value: any }; + }; + "i/registry/get": { req: { key: string; scope?: string[] }; res: any }; + "i/registry/keys-with-type": { + req: { scope?: string[] }; + res: Record< + string, + "null" | "array" | "number" | "string" | "boolean" | "object" + >; + }; + "i/registry/keys": { req: { scope?: string[] }; res: string[] }; + "i/registry/remove": { req: { key: string; scope?: string[] }; res: null }; + "i/registry/scopes": { req: NoParams; res: string[][] }; + "i/registry/set": { + req: { key: string; value: any; scope?: string[] }; + res: null; + }; + "i/revoke-token": { req: TODO; res: TODO }; + "i/signin-history": { + req: { limit?: number; sinceId?: Signin["id"]; untilId?: Signin["id"] }; + res: Signin[]; + }; + "i/unpin": { req: { noteId: Note["id"] }; res: MeDetailed }; + "i/update-email": { + req: { + password: string; + email?: string | null; + }; + res: MeDetailed; + }; + "i/update": { + req: { + name?: string | null; + description?: string | null; + lang?: string | null; + location?: string | null; + birthday?: string | null; + avatarId?: DriveFile["id"] | null; + bannerId?: DriveFile["id"] | null; + fields?: { + name: string; + value: string; + }[]; + isLocked?: boolean; + isExplorable?: boolean; + hideOnlineStatus?: boolean; + carefulBot?: boolean; + autoAcceptFollowed?: boolean; + noCrawle?: boolean; + isBot?: boolean; + isCat?: boolean; + injectFeaturedNote?: boolean; + receiveAnnouncementEmail?: boolean; + alwaysMarkNsfw?: boolean; + mutedWords?: string[][]; + mutingNotificationTypes?: Notification["type"][]; + emailNotificationTypes?: string[]; + }; + res: MeDetailed; + }; + "i/user-group-invites": { req: TODO; res: TODO }; + "i/2fa/done": { req: TODO; res: TODO }; + "i/2fa/key-done": { req: TODO; res: TODO }; + "i/2fa/password-less": { req: TODO; res: TODO }; + "i/2fa/register-key": { req: TODO; res: TODO }; + "i/2fa/register": { req: TODO; res: TODO }; + "i/2fa/remove-key": { req: TODO; res: TODO }; + "i/2fa/unregister": { req: TODO; res: TODO }; // messaging - 'messaging/history': { req: { limit?: number; group?: boolean; }; res: MessagingMessage[]; }; - 'messaging/messages': { req: { userId?: User['id']; groupId?: UserGroup['id']; limit?: number; sinceId?: MessagingMessage['id']; untilId?: MessagingMessage['id']; markAsRead?: boolean; }; res: MessagingMessage[]; }; - 'messaging/messages/create': { req: { userId?: User['id']; groupId?: UserGroup['id']; text?: string; fileId?: DriveFile['id']; }; res: MessagingMessage; }; - 'messaging/messages/delete': { req: { messageId: MessagingMessage['id']; }; res: null; }; - 'messaging/messages/read': { req: { messageId: MessagingMessage['id']; }; res: null; }; + "messaging/history": { + req: { limit?: number; group?: boolean }; + res: MessagingMessage[]; + }; + "messaging/messages": { + req: { + userId?: User["id"]; + groupId?: UserGroup["id"]; + limit?: number; + sinceId?: MessagingMessage["id"]; + untilId?: MessagingMessage["id"]; + markAsRead?: boolean; + }; + res: MessagingMessage[]; + }; + "messaging/messages/create": { + req: { + userId?: User["id"]; + groupId?: UserGroup["id"]; + text?: string; + fileId?: DriveFile["id"]; + }; + res: MessagingMessage; + }; + "messaging/messages/delete": { + req: { messageId: MessagingMessage["id"] }; + res: null; + }; + "messaging/messages/read": { + req: { messageId: MessagingMessage["id"] }; + res: null; + }; // meta - 'meta': { req: { detail?: boolean; }; res: { - $switch: { - $cases: [[ - { detail: true; }, - DetailedInstanceMetadata, - ], [ - { detail: false; }, - LiteInstanceMetadata, - ], [ - { detail: boolean; }, - LiteInstanceMetadata | DetailedInstanceMetadata, - ]]; - $default: LiteInstanceMetadata; + meta: { + req: { detail?: boolean }; + res: { + $switch: { + $cases: [ + [{ detail: true }, DetailedInstanceMetadata], + [{ detail: false }, LiteInstanceMetadata], + [ + { detail: boolean }, + LiteInstanceMetadata | DetailedInstanceMetadata, + ], + ]; + $default: LiteInstanceMetadata; + }; }; - }; }; + }; // miauth - 'miauth/gen-token': { req: TODO; res: TODO; }; + "miauth/gen-token": { req: TODO; res: TODO }; // mute - 'mute/create': { req: TODO; res: TODO; }; - 'mute/delete': { req: { userId: User['id'] }; res: null; }; - 'mute/list': { req: TODO; res: TODO; }; + "mute/create": { req: TODO; res: TODO }; + "mute/delete": { req: { userId: User["id"] }; res: null }; + "mute/list": { req: TODO; res: TODO }; // my - 'my/apps': { req: TODO; res: TODO; }; + "my/apps": { req: TODO; res: TODO }; // notes - 'notes': { req: { limit?: number; sinceId?: Note['id']; untilId?: Note['id']; }; res: Note[]; }; - 'notes/children': { req: { noteId: Note['id']; limit?: number; sinceId?: Note['id']; untilId?: Note['id']; }; res: Note[]; }; - 'notes/clips': { req: TODO; res: TODO; }; - 'notes/conversation': { req: TODO; res: TODO; }; - 'notes/create': { req: { - visibility?: 'public' | 'home' | 'followers' | 'specified', - visibleUserIds?: User['id'][]; - text?: null | string; - cw?: null | string; - viaMobile?: boolean; - localOnly?: boolean; - fileIds?: DriveFile['id'][]; - replyId?: null | Note['id']; - renoteId?: null | Note['id']; - channelId?: null | Channel['id']; - poll?: null | { - choices: string[]; - multiple?: boolean; - expiresAt?: null | number; - expiredAfter?: null | number; + notes: { + req: { limit?: number; sinceId?: Note["id"]; untilId?: Note["id"] }; + res: Note[]; + }; + "notes/children": { + req: { + noteId: Note["id"]; + limit?: number; + sinceId?: Note["id"]; + untilId?: Note["id"]; }; - }; res: { createdNote: Note }; }; - 'notes/delete': { req: { noteId: Note['id']; }; res: null; }; - 'notes/favorites/create': { req: { noteId: Note['id']; }; res: null; }; - 'notes/favorites/delete': { req: { noteId: Note['id']; }; res: null; }; - 'notes/featured': { req: TODO; res: Note[]; }; - 'notes/global-timeline': { req: { limit?: number; sinceId?: Note['id']; untilId?: Note['id']; sinceDate?: number; untilDate?: number; }; res: Note[]; }; - 'notes/recommended-timeline': { req: { limit?: number; sinceId?: Note['id']; untilId?: Note['id']; sinceDate?: number; untilDate?: number; }; res: Note[]; }; - 'notes/hybrid-timeline': { req: { limit?: number; sinceId?: Note['id']; untilId?: Note['id']; sinceDate?: number; untilDate?: number; }; res: Note[]; }; - 'notes/local-timeline': { req: { limit?: number; sinceId?: Note['id']; untilId?: Note['id']; sinceDate?: number; untilDate?: number; }; res: Note[]; }; - 'notes/mentions': { req: { following?: boolean; limit?: number; sinceId?: Note['id']; untilId?: Note['id']; }; res: Note[]; }; - 'notes/polls/recommendation': { req: TODO; res: TODO; }; - 'notes/polls/vote': { req: { noteId: Note['id']; choice: number; }; res: null; }; - 'notes/reactions': { req: { noteId: Note['id']; type?: string | null; limit?: number; }; res: NoteReaction[]; }; - 'notes/reactions/create': { req: { noteId: Note['id']; reaction: string; }; res: null; }; - 'notes/reactions/delete': { req: { noteId: Note['id']; }; res: null; }; - 'notes/renotes': { req: { limit?: number; sinceId?: Note['id']; untilId?: Note['id']; noteId: Note['id']; }; res: Note[]; }; - 'notes/replies': { req: { limit?: number; sinceId?: Note['id']; untilId?: Note['id']; noteId: Note['id']; }; res: Note[]; }; - 'notes/search-by-tag': { req: TODO; res: TODO; }; - 'notes/search': { req: TODO; res: TODO; }; - 'notes/show': { req: { noteId: Note['id']; }; res: Note; }; - 'notes/state': { req: TODO; res: TODO; }; - 'notes/timeline': { req: { limit?: number; sinceId?: Note['id']; untilId?: Note['id']; sinceDate?: number; untilDate?: number; }; res: Note[]; }; - 'notes/unrenote': { req: { noteId: Note['id']; }; res: null; }; - 'notes/user-list-timeline': { req: { listId: UserList['id']; limit?: number; sinceId?: Note['id']; untilId?: Note['id']; sinceDate?: number; untilDate?: number; }; res: Note[]; }; - 'notes/watching/create': { req: TODO; res: TODO; }; - 'notes/watching/delete': { req: { noteId: Note['id']; }; res: null; }; + res: Note[]; + }; + "notes/clips": { req: TODO; res: TODO }; + "notes/conversation": { req: TODO; res: TODO }; + "notes/create": { + req: { + visibility?: "public" | "home" | "followers" | "specified"; + visibleUserIds?: User["id"][]; + text?: null | string; + cw?: null | string; + viaMobile?: boolean; + localOnly?: boolean; + fileIds?: DriveFile["id"][]; + replyId?: null | Note["id"]; + renoteId?: null | Note["id"]; + channelId?: null | Channel["id"]; + poll?: null | { + choices: string[]; + multiple?: boolean; + expiresAt?: null | number; + expiredAfter?: null | number; + }; + }; + res: { createdNote: Note }; + }; + "notes/delete": { req: { noteId: Note["id"] }; res: null }; + "notes/favorites/create": { req: { noteId: Note["id"] }; res: null }; + "notes/favorites/delete": { req: { noteId: Note["id"] }; res: null }; + "notes/featured": { req: TODO; res: Note[] }; + "notes/global-timeline": { + req: { + limit?: number; + sinceId?: Note["id"]; + untilId?: Note["id"]; + sinceDate?: number; + untilDate?: number; + }; + res: Note[]; + }; + "notes/recommended-timeline": { + req: { + limit?: number; + sinceId?: Note["id"]; + untilId?: Note["id"]; + sinceDate?: number; + untilDate?: number; + }; + res: Note[]; + }; + "notes/hybrid-timeline": { + req: { + limit?: number; + sinceId?: Note["id"]; + untilId?: Note["id"]; + sinceDate?: number; + untilDate?: number; + }; + res: Note[]; + }; + "notes/local-timeline": { + req: { + limit?: number; + sinceId?: Note["id"]; + untilId?: Note["id"]; + sinceDate?: number; + untilDate?: number; + }; + res: Note[]; + }; + "notes/mentions": { + req: { + following?: boolean; + limit?: number; + sinceId?: Note["id"]; + untilId?: Note["id"]; + }; + res: Note[]; + }; + "notes/polls/recommendation": { req: TODO; res: TODO }; + "notes/polls/vote": { + req: { noteId: Note["id"]; choice: number }; + res: null; + }; + "notes/reactions": { + req: { noteId: Note["id"]; type?: string | null; limit?: number }; + res: NoteReaction[]; + }; + "notes/reactions/create": { + req: { noteId: Note["id"]; reaction: string }; + res: null; + }; + "notes/reactions/delete": { req: { noteId: Note["id"] }; res: null }; + "notes/renotes": { + req: { + limit?: number; + sinceId?: Note["id"]; + untilId?: Note["id"]; + noteId: Note["id"]; + }; + res: Note[]; + }; + "notes/replies": { + req: { + limit?: number; + sinceId?: Note["id"]; + untilId?: Note["id"]; + noteId: Note["id"]; + }; + res: Note[]; + }; + "notes/search-by-tag": { req: TODO; res: TODO }; + "notes/search": { req: TODO; res: TODO }; + "notes/show": { req: { noteId: Note["id"] }; res: Note }; + "notes/state": { req: TODO; res: TODO }; + "notes/timeline": { + req: { + limit?: number; + sinceId?: Note["id"]; + untilId?: Note["id"]; + sinceDate?: number; + untilDate?: number; + }; + res: Note[]; + }; + "notes/unrenote": { req: { noteId: Note["id"] }; res: null }; + "notes/user-list-timeline": { + req: { + listId: UserList["id"]; + limit?: number; + sinceId?: Note["id"]; + untilId?: Note["id"]; + sinceDate?: number; + untilDate?: number; + }; + res: Note[]; + }; + "notes/watching/create": { req: TODO; res: TODO }; + "notes/watching/delete": { req: { noteId: Note["id"] }; res: null }; // notifications - 'notifications/create': { req: { body: string; header?: string | null; icon?: string | null; }; res: null; }; - 'notifications/mark-all-as-read': { req: NoParams; res: null; }; - 'notifications/read': { req: { notificationId: Notification['id']; }; res: null; }; + "notifications/create": { + req: { body: string; header?: string | null; icon?: string | null }; + res: null; + }; + "notifications/mark-all-as-read": { req: NoParams; res: null }; + "notifications/read": { + req: { notificationId: Notification["id"] }; + res: null; + }; // page-push - 'page-push': { req: { pageId: Page['id']; event: string; var?: any; }; res: null; }; + "page-push": { + req: { pageId: Page["id"]; event: string; var?: any }; + res: null; + }; // pages - 'pages/create': { req: TODO; res: Page; }; - 'pages/delete': { req: { pageId: Page['id']; }; res: null; }; - 'pages/featured': { req: NoParams; res: Page[]; }; - 'pages/like': { req: { pageId: Page['id']; }; res: null; }; - 'pages/show': { req: { pageId?: Page['id']; name?: string; username?: string; }; res: Page; }; - 'pages/unlike': { req: { pageId: Page['id']; }; res: null; }; - 'pages/update': { req: TODO; res: null; }; + "pages/create": { req: TODO; res: Page }; + "pages/delete": { req: { pageId: Page["id"] }; res: null }; + "pages/featured": { req: NoParams; res: Page[] }; + "pages/like": { req: { pageId: Page["id"] }; res: null }; + "pages/show": { + req: { pageId?: Page["id"]; name?: string; username?: string }; + res: Page; + }; + "pages/unlike": { req: { pageId: Page["id"] }; res: null }; + "pages/update": { req: TODO; res: null }; // ping - 'ping': { req: NoParams; res: { pong: number; }; }; + ping: { req: NoParams; res: { pong: number } }; // pinned-users - 'pinned-users': { req: TODO; res: TODO; }; + "pinned-users": { req: TODO; res: TODO }; // promo - 'promo/read': { req: TODO; res: TODO; }; + "promo/read": { req: TODO; res: TODO }; // request-reset-password - 'request-reset-password': { req: { username: string; email: string; }; res: null; }; + "request-reset-password": { + req: { username: string; email: string }; + res: null; + }; // reset-password - 'reset-password': { req: { token: string; password: string; }; res: null; }; + "reset-password": { req: { token: string; password: string }; res: null }; // room - 'room/show': { req: TODO; res: TODO; }; - 'room/update': { req: TODO; res: TODO; }; + "room/show": { req: TODO; res: TODO }; + "room/update": { req: TODO; res: TODO }; // stats - 'stats': { req: NoParams; res: Stats; }; + stats: { req: NoParams; res: Stats }; // server-info - 'server-info': { req: NoParams; res: ServerInfo; }; + "server-info": { req: NoParams; res: ServerInfo }; // ck specific - 'latest-version': { req: NoParams; res: TODO; }; + "latest-version": { req: NoParams; res: TODO }; // sw - 'sw/register': { req: TODO; res: TODO; }; + "sw/register": { req: TODO; res: TODO }; // username - 'username/available': { req: { username: string; }; res: { available: boolean; }; }; + "username/available": { + req: { username: string }; + res: { available: boolean }; + }; // users - 'users': { req: { limit?: number; offset?: number; sort?: UserSorting; origin?: OriginType; }; res: User[]; }; - 'users/clips': { req: TODO; res: TODO; }; - 'users/followers': { req: { userId?: User['id']; username?: User['username']; host?: User['host'] | null; limit?: number; sinceId?: Following['id']; untilId?: Following['id']; }; res: FollowingFollowerPopulated[]; }; - 'users/following': { req: { userId?: User['id']; username?: User['username']; host?: User['host'] | null; limit?: number; sinceId?: Following['id']; untilId?: Following['id']; }; res: FollowingFolloweePopulated[]; }; - 'users/gallery/posts': { req: TODO; res: TODO; }; - 'users/get-frequently-replied-users': { req: TODO; res: TODO; }; - 'users/groups/create': { req: TODO; res: TODO; }; - 'users/groups/delete': { req: { groupId: UserGroup['id'] }; res: null; }; - 'users/groups/invitations/accept': { req: TODO; res: TODO; }; - 'users/groups/invitations/reject': { req: TODO; res: TODO; }; - 'users/groups/invite': { req: TODO; res: TODO; }; - 'users/groups/joined': { req: TODO; res: TODO; }; - 'users/groups/owned': { req: TODO; res: TODO; }; - 'users/groups/pull': { req: TODO; res: TODO; }; - 'users/groups/show': { req: TODO; res: TODO; }; - 'users/groups/transfer': { req: TODO; res: TODO; }; - 'users/groups/update': { req: TODO; res: TODO; }; - 'users/lists/create': { req: { name: string; }; res: UserList; }; - 'users/lists/delete': { req: { listId: UserList['id']; }; res: null; }; - 'users/lists/list': { req: NoParams; res: UserList[]; }; - 'users/lists/pull': { req: { listId: UserList['id']; userId: User['id']; }; res: null; }; - 'users/lists/push': { req: { listId: UserList['id']; userId: User['id']; }; res: null; }; - 'users/lists/show': { req: { listId: UserList['id']; }; res: UserList; }; - 'users/lists/update': { req: { listId: UserList['id']; name: string; }; res: UserList; }; - 'users/notes': { req: { userId: User['id']; limit?: number; sinceId?: Note['id']; untilId?: Note['id']; sinceDate?: number; untilDate?: number; }; res: Note[]; }; - 'users/pages': { req: TODO; res: TODO; }; - 'users/recommendation': { req: TODO; res: TODO; }; - 'users/relation': { req: TODO; res: TODO; }; - 'users/report-abuse': { req: TODO; res: TODO; }; - 'users/search-by-username-and-host': { req: TODO; res: TODO; }; - 'users/search': { req: TODO; res: TODO; }; - 'users/show': { req: ShowUserReq | { userIds: User['id'][]; }; res: { - $switch: { - $cases: [[ - { userIds: User['id'][]; }, - UserDetailed[], - ]]; - $default: UserDetailed; + users: { + req: { + limit?: number; + offset?: number; + sort?: UserSorting; + origin?: OriginType; }; - }; }; - 'users/stats': { req: TODO; res: TODO; }; + res: User[]; + }; + "users/clips": { req: TODO; res: TODO }; + "users/followers": { + req: { + userId?: User["id"]; + username?: User["username"]; + host?: User["host"] | null; + limit?: number; + sinceId?: Following["id"]; + untilId?: Following["id"]; + }; + res: FollowingFollowerPopulated[]; + }; + "users/following": { + req: { + userId?: User["id"]; + username?: User["username"]; + host?: User["host"] | null; + limit?: number; + sinceId?: Following["id"]; + untilId?: Following["id"]; + }; + res: FollowingFolloweePopulated[]; + }; + "users/gallery/posts": { req: TODO; res: TODO }; + "users/get-frequently-replied-users": { req: TODO; res: TODO }; + "users/groups/create": { req: TODO; res: TODO }; + "users/groups/delete": { req: { groupId: UserGroup["id"] }; res: null }; + "users/groups/invitations/accept": { req: TODO; res: TODO }; + "users/groups/invitations/reject": { req: TODO; res: TODO }; + "users/groups/invite": { req: TODO; res: TODO }; + "users/groups/joined": { req: TODO; res: TODO }; + "users/groups/owned": { req: TODO; res: TODO }; + "users/groups/pull": { req: TODO; res: TODO }; + "users/groups/show": { req: TODO; res: TODO }; + "users/groups/transfer": { req: TODO; res: TODO }; + "users/groups/update": { req: TODO; res: TODO }; + "users/lists/create": { req: { name: string }; res: UserList }; + "users/lists/delete": { req: { listId: UserList["id"] }; res: null }; + "users/lists/list": { req: NoParams; res: UserList[] }; + "users/lists/pull": { + req: { listId: UserList["id"]; userId: User["id"] }; + res: null; + }; + "users/lists/push": { + req: { listId: UserList["id"]; userId: User["id"] }; + res: null; + }; + "users/lists/show": { req: { listId: UserList["id"] }; res: UserList }; + "users/lists/update": { + req: { listId: UserList["id"]; name: string }; + res: UserList; + }; + "users/notes": { + req: { + userId: User["id"]; + limit?: number; + sinceId?: Note["id"]; + untilId?: Note["id"]; + sinceDate?: number; + untilDate?: number; + }; + res: Note[]; + }; + "users/pages": { req: TODO; res: TODO }; + "users/recommendation": { req: TODO; res: TODO }; + "users/relation": { req: TODO; res: TODO }; + "users/report-abuse": { req: TODO; res: TODO }; + "users/search-by-username-and-host": { req: TODO; res: TODO }; + "users/search": { req: TODO; res: TODO }; + "users/show": { + req: ShowUserReq | { userIds: User["id"][] }; + res: { + $switch: { + $cases: [[{ userIds: User["id"][] }, UserDetailed[]]]; + $default: UserDetailed; + }; + }; + }; + "users/stats": { req: TODO; res: TODO }; }; diff --git a/packages/calckey-js/src/consts.ts b/packages/calckey-js/src/consts.ts index 261ecd33f4..be33c0d3e3 100644 --- a/packages/calckey-js/src/consts.ts +++ b/packages/calckey-js/src/consts.ts @@ -1,42 +1,60 @@ -export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app'] as const; +export const notificationTypes = [ + "follow", + "mention", + "reply", + "renote", + "quote", + "reaction", + "pollVote", + "pollEnded", + "receiveFollowRequest", + "followRequestAccepted", + "groupInvited", + "app", +] as const; -export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const; +export const noteVisibilities = [ + "public", + "home", + "followers", + "specified", +] as const; -export const mutedNoteReasons = ['word', 'manual', 'spam', 'other'] as const; +export const mutedNoteReasons = ["word", "manual", "spam", "other"] as const; -export const ffVisibility = ['public', 'followers', 'private'] as const; +export const ffVisibility = ["public", "followers", "private"] as const; export const permissions = [ - 'read:account', - 'write:account', - 'read:blocks', - 'write:blocks', - 'read:drive', - 'write:drive', - 'read:favorites', - 'write:favorites', - 'read:following', - 'write:following', - 'read:messaging', - 'write:messaging', - 'read:mutes', - 'write:mutes', - 'write:notes', - 'read:notifications', - 'write:notifications', - 'read:reactions', - 'write:reactions', - 'write:votes', - 'read:pages', - 'write:pages', - 'write:page-likes', - 'read:page-likes', - 'read:user-groups', - 'write:user-groups', - 'read:channels', - 'write:channels', - 'read:gallery', - 'write:gallery', - 'read:gallery-likes', - 'write:gallery-likes', + "read:account", + "write:account", + "read:blocks", + "write:blocks", + "read:drive", + "write:drive", + "read:favorites", + "write:favorites", + "read:following", + "write:following", + "read:messaging", + "write:messaging", + "read:mutes", + "write:mutes", + "write:notes", + "read:notifications", + "write:notifications", + "read:reactions", + "write:reactions", + "write:votes", + "read:pages", + "write:pages", + "write:page-likes", + "read:page-likes", + "read:user-groups", + "write:user-groups", + "read:channels", + "write:channels", + "read:gallery", + "write:gallery", + "read:gallery-likes", + "write:gallery-likes", ]; diff --git a/packages/calckey-js/src/entities.ts b/packages/calckey-js/src/entities.ts index bade8b4b19..b15dd8fca7 100644 --- a/packages/calckey-js/src/entities.ts +++ b/packages/calckey-js/src/entities.ts @@ -11,7 +11,7 @@ export type UserLite = { username: string; host: string | null; name: string; - onlineStatus: 'online' | 'active' | 'offline' | 'unknown'; + onlineStatus: "online" | "active" | "offline" | "unknown"; avatarUrl: string; avatarBlurhash: string; alsoKnownAs: string[]; @@ -21,12 +21,12 @@ export type UserLite = { url: string; }[]; instance?: { - name: Instance['name']; - softwareName: Instance['softwareName']; - softwareVersion: Instance['softwareVersion']; - iconUrl: Instance['iconUrl']; - faviconUrl: Instance['faviconUrl']; - themeColor: Instance['themeColor']; + name: Instance["name"]; + softwareName: Instance["softwareName"]; + softwareVersion: Instance["softwareVersion"]; + iconUrl: Instance["iconUrl"]; + faviconUrl: Instance["faviconUrl"]; + themeColor: Instance["themeColor"]; }; }; @@ -37,8 +37,8 @@ export type UserDetailed = UserLite & { birthday: string | null; createdAt: DateString; description: string | null; - ffVisibility: 'public' | 'followers' | 'private'; - fields: {name: string; value: string}[]; + ffVisibility: "public" | "followers" | "private"; + fields: { name: string; value: string }[]; followersCount: number; followingCount: number; hasPendingFollowRequestFromYou: boolean; @@ -77,12 +77,12 @@ export type UserList = { id: ID; createdAt: DateString; name: string; - userIds: User['id'][]; + userIds: User["id"][]; }; export type MeDetailed = UserDetailed & { - avatarId: DriveFile['id']; - bannerId: DriveFile['id']; + avatarId: DriveFile["id"]; + bannerId: DriveFile["id"]; autoAcceptFollowed: boolean; alwaysMarkNsfw: boolean; carefulBot: boolean; @@ -133,15 +133,15 @@ export type Note = { text: string | null; cw: string | null; user: User; - userId: User['id']; + userId: User["id"]; reply?: Note; - replyId: Note['id']; + replyId: Note["id"]; renote?: Note; - renoteId: Note['id']; + renoteId: Note["id"]; files: DriveFile[]; - fileIds: DriveFile['id'][]; - visibility: 'public' | 'home' | 'followers' | 'specified'; - visibleUserIds?: User['id'][]; + fileIds: DriveFile["id"][]; + visibility: "public" | "home" | "followers" | "specified"; + visibleUserIds?: User["id"][]; localOnly?: boolean; myReaction?: string; reactions: Record; @@ -176,75 +176,87 @@ export type Notification = { id: ID; createdAt: DateString; isRead: boolean; -} & ({ - type: 'reaction'; - reaction: string; - user: User; - userId: User['id']; - note: Note; -} | { - type: 'reply'; - user: User; - userId: User['id']; - note: Note; -} | { - type: 'renote'; - user: User; - userId: User['id']; - note: Note; -} | { - type: 'quote'; - user: User; - userId: User['id']; - note: Note; -} | { - type: 'mention'; - user: User; - userId: User['id']; - note: Note; -} | { - type: 'pollVote'; - user: User; - userId: User['id']; - note: Note; -} | { - type: 'follow'; - user: User; - userId: User['id']; -} | { - type: 'followRequestAccepted'; - user: User; - userId: User['id']; -} | { - type: 'receiveFollowRequest'; - user: User; - userId: User['id']; -} | { - type: 'groupInvited'; - invitation: UserGroup; - user: User; - userId: User['id']; -} | { - type: 'app'; - header?: string | null; - body: string; - icon?: string | null; -}); +} & ( + | { + type: "reaction"; + reaction: string; + user: User; + userId: User["id"]; + note: Note; + } + | { + type: "reply"; + user: User; + userId: User["id"]; + note: Note; + } + | { + type: "renote"; + user: User; + userId: User["id"]; + note: Note; + } + | { + type: "quote"; + user: User; + userId: User["id"]; + note: Note; + } + | { + type: "mention"; + user: User; + userId: User["id"]; + note: Note; + } + | { + type: "pollVote"; + user: User; + userId: User["id"]; + note: Note; + } + | { + type: "follow"; + user: User; + userId: User["id"]; + } + | { + type: "followRequestAccepted"; + user: User; + userId: User["id"]; + } + | { + type: "receiveFollowRequest"; + user: User; + userId: User["id"]; + } + | { + type: "groupInvited"; + invitation: UserGroup; + user: User; + userId: User["id"]; + } + | { + type: "app"; + header?: string | null; + body: string; + icon?: string | null; + } +); export type MessagingMessage = { id: ID; createdAt: DateString; file: DriveFile | null; - fileId: DriveFile['id'] | null; + fileId: DriveFile["id"] | null; isRead: boolean; - reads: User['id'][]; + reads: User["id"][]; text: string | null; user: User; - userId: User['id']; + userId: User["id"]; recipient?: User | null; - recipientId: User['id'] | null; + recipientId: User["id"] | null; group?: UserGroup | null; - groupId: UserGroup['id'] | null; + groupId: UserGroup["id"] | null; }; export type CustomEmoji = { @@ -325,7 +337,7 @@ export type Page = { id: ID; createdAt: DateString; updatedAt: DateString; - userId: User['id']; + userId: User["id"]; user: User; content: Record[]; variables: Record[]; @@ -336,7 +348,7 @@ export type Page = { alignCenter: boolean; font: string; script: string; - eyeCatchingImageId: DriveFile['id'] | null; + eyeCatchingImageId: DriveFile["id"] | null; eyeCatchingImage: DriveFile | null; attachedFiles: any; likedCount: number; @@ -344,10 +356,10 @@ export type Page = { }; export type PageEvent = { - pageId: Page['id']; + pageId: Page["id"]; event: string; var: any; - userId: User['id']; + userId: User["id"]; user: User; }; @@ -367,7 +379,7 @@ export type Antenna = { name: string; keywords: string[][]; // TODO excludeKeywords: string[][]; // TODO - src: 'home' | 'all' | 'users' | 'list' | 'group' | 'instances'; + src: "home" | "all" | "users" | "list" | "group" | "instances"; userListId: ID | null; // TODO userGroupId: ID | null; // TODO users: string[]; // TODO @@ -394,7 +406,7 @@ export type Clip = TODO; export type NoteFavorite = { id: ID; createdAt: DateString; - noteId: Note['id']; + noteId: Note["id"]; note: Note; }; @@ -412,8 +424,8 @@ export type Channel = { export type Following = { id: ID; createdAt: DateString; - followerId: User['id']; - followeeId: User['id']; + followerId: User["id"]; + followeeId: User["id"]; }; export type FollowingFolloweePopulated = Following & { @@ -427,7 +439,7 @@ export type FollowingFollowerPopulated = Following & { export type Blocking = { id: ID; createdAt: DateString; - blockeeId: User['id']; + blockeeId: User["id"]; blockee: UserDetailed; }; @@ -469,10 +481,10 @@ export type Signin = { }; export type UserSorting = - | '+follower' - | '-follower' - | '+createdAt' - | '-createdAt' - | '+updatedAt' - | '-updatedAt'; -export type OriginType = 'combined' | 'local' | 'remote'; + | "+follower" + | "-follower" + | "+createdAt" + | "-createdAt" + | "+updatedAt" + | "-updatedAt"; +export type OriginType = "combined" | "local" | "remote"; diff --git a/packages/calckey-js/src/index.ts b/packages/calckey-js/src/index.ts index f431d65cc7..128ed8c535 100644 --- a/packages/calckey-js/src/index.ts +++ b/packages/calckey-js/src/index.ts @@ -1,16 +1,10 @@ -import { Endpoints } from './api.types'; -import Stream, { Connection } from './streaming'; -import { Channels } from './streaming.types'; -import { Acct } from './acct'; -import * as consts from './consts'; +import { Endpoints } from "./api.types"; +import Stream, { Connection } from "./streaming"; +import { Channels } from "./streaming.types"; +import { Acct } from "./acct"; +import * as consts from "./consts"; -export { - Endpoints, - Stream, - Connection as ChannelConnection, - Channels, - Acct, -}; +export { Endpoints, Stream, Connection as ChannelConnection, Channels, Acct }; export const permissions = consts.permissions; export const notificationTypes = consts.notificationTypes; @@ -21,6 +15,6 @@ export const ffVisibility = consts.ffVisibility; // api extractor not supported yet //export * as api from './api'; //export * as entities from './entities'; -import * as api from './api'; -import * as entities from './entities'; +import * as api from "./api"; +import * as entities from "./entities"; export { api, entities }; diff --git a/packages/calckey-js/src/streaming.ts b/packages/calckey-js/src/streaming.ts index 14be97b436..80a3d6e8c3 100644 --- a/packages/calckey-js/src/streaming.ts +++ b/packages/calckey-js/src/streaming.ts @@ -1,17 +1,22 @@ -import autobind from 'autobind-decorator'; -import { EventEmitter } from 'eventemitter3'; -import ReconnectingWebsocket from 'reconnecting-websocket'; -import { BroadcastEvents, Channels } from './streaming.types'; +import autobind from "autobind-decorator"; +import { EventEmitter } from "eventemitter3"; +import ReconnectingWebsocket from "reconnecting-websocket"; +import { BroadcastEvents, Channels } from "./streaming.types"; -export function urlQuery(obj: Record): string { +export function urlQuery( + obj: Record, +): string { const params = Object.entries(obj) - .filter(([, v]) => Array.isArray(v) ? v.length : v !== undefined) + .filter(([, v]) => (Array.isArray(v) ? v.length : v !== undefined)) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - .reduce((a, [k, v]) => (a[k] = v!, a), {} as Record); + .reduce( + (a, [k, v]) => ((a[k] = v!), a), + {} as Record, + ); return Object.entries(params) .map((e) => `${e[0]}=${encodeURIComponent(e[1])}`) - .join('&'); + .join("&"); } type AnyOf> = T[keyof T]; @@ -26,17 +31,21 @@ type StreamEvents = { */ export default class Stream extends EventEmitter { private stream: ReconnectingWebsocket; - public state: 'initializing' | 'reconnecting' | 'connected' = 'initializing'; + public state: "initializing" | "reconnecting" | "connected" = "initializing"; private sharedConnectionPools: Pool[] = []; private sharedConnections: SharedConnection[] = []; private nonSharedConnections: NonSharedConnection[] = []; private idCounter = 0; - constructor(origin: string, user: { token: string; } | null, options?: { - WebSocket?: any; - }) { + constructor( + origin: string, + user: { token: string } | null, + options?: { + WebSocket?: any; + }, + ) { super(); - options = options || { }; + options = options || {}; const query = urlQuery({ i: user?.token, @@ -45,15 +54,21 @@ export default class Stream extends EventEmitter { _t: Date.now(), }); - const wsOrigin = origin.replace('http://', 'ws://').replace('https://', 'wss://'); + const wsOrigin = origin + .replace("http://", "ws://") + .replace("https://", "wss://"); - this.stream = new ReconnectingWebsocket(`${wsOrigin}/streaming?${query}`, '', { - minReconnectionDelay: 1, // https://github.com/pladaria/reconnecting-websocket/issues/91 - WebSocket: options.WebSocket, - }); - this.stream.addEventListener('open', this.onOpen); - this.stream.addEventListener('close', this.onClose); - this.stream.addEventListener('message', this.onMessage); + this.stream = new ReconnectingWebsocket( + `${wsOrigin}/streaming?${query}`, + "", + { + minReconnectionDelay: 1, // https://github.com/pladaria/reconnecting-websocket/issues/91 + WebSocket: options.WebSocket, + }, + ); + this.stream.addEventListener("open", this.onOpen); + this.stream.addEventListener("close", this.onClose); + this.stream.addEventListener("message", this.onMessage); } @autobind @@ -62,7 +77,11 @@ export default class Stream extends EventEmitter { } @autobind - public useChannel(channel: C, params?: Channels[C]['params'], name?: string): Connection { + public useChannel( + channel: C, + params?: Channels[C]["params"], + name?: string, + ): Connection { if (params) { return this.connectToChannel(channel, params); } else { @@ -71,8 +90,11 @@ export default class Stream extends EventEmitter { } @autobind - private useSharedConnection(channel: C, name?: string): SharedConnection { - let pool = this.sharedConnectionPools.find(p => p.channel === channel); + private useSharedConnection( + channel: C, + name?: string, + ): SharedConnection { + let pool = this.sharedConnectionPools.find((p) => p.channel === channel); if (pool == null) { pool = new Pool(this, channel, this.genId()); @@ -86,24 +108,38 @@ export default class Stream extends EventEmitter { @autobind public removeSharedConnection(connection: SharedConnection): void { - this.sharedConnections = this.sharedConnections.filter(c => c !== connection); + this.sharedConnections = this.sharedConnections.filter( + (c) => c !== connection, + ); } @autobind public removeSharedConnectionPool(pool: Pool): void { - this.sharedConnectionPools = this.sharedConnectionPools.filter(p => p !== pool); + this.sharedConnectionPools = this.sharedConnectionPools.filter( + (p) => p !== pool, + ); } @autobind - private connectToChannel(channel: C, params: Channels[C]['params']): NonSharedConnection { - const connection = new NonSharedConnection(this, channel, this.genId(), params); + private connectToChannel( + channel: C, + params: Channels[C]["params"], + ): NonSharedConnection { + const connection = new NonSharedConnection( + this, + channel, + this.genId(), + params, + ); this.nonSharedConnections.push(connection); return connection; } @autobind public disconnectToChannel(connection: NonSharedConnection): void { - this.nonSharedConnections = this.nonSharedConnections.filter(c => c !== connection); + this.nonSharedConnections = this.nonSharedConnections.filter( + (c) => c !== connection, + ); } /** @@ -111,10 +147,10 @@ export default class Stream extends EventEmitter { */ @autobind private onOpen(): void { - const isReconnect = this.state === 'reconnecting'; + const isReconnect = this.state === "reconnecting"; - this.state = 'connected'; - this.emit('_connected_'); + this.state = "connected"; + this.emit("_connected_"); // チャンネル再接続 if (isReconnect) { @@ -128,9 +164,9 @@ export default class Stream extends EventEmitter { */ @autobind private onClose(): void { - if (this.state === 'connected') { - this.state = 'reconnecting'; - this.emit('_disconnected_'); + if (this.state === "connected") { + this.state = "reconnecting"; + this.emit("_disconnected_"); } } @@ -138,18 +174,18 @@ export default class Stream extends EventEmitter { * Callback of when received a message from connection */ @autobind - private onMessage(message: { data: string; }): void { + private onMessage(message: { data: string }): void { const { type, body } = JSON.parse(message.data); - if (type === 'channel') { + if (type === "channel") { const id = body.id; let connections: Connection[]; - connections = this.sharedConnections.filter(c => c.id === id); + connections = this.sharedConnections.filter((c) => c.id === id); if (connections.length === 0) { - const found = this.nonSharedConnections.find(c => c.id === id); + const found = this.nonSharedConnections.find((c) => c.id === id); if (found) { connections = [found]; } @@ -169,10 +205,13 @@ export default class Stream extends EventEmitter { */ @autobind public send(typeOrPayload: any, payload?: any): void { - const data = payload === undefined ? typeOrPayload : { - type: typeOrPayload, - body: payload, - }; + const data = + payload === undefined + ? typeOrPayload + : { + type: typeOrPayload, + body: payload, + }; this.stream.send(JSON.stringify(data)); } @@ -201,7 +240,7 @@ class Pool { this.stream = stream; this.id = id; - this.stream.on('_disconnected_', this.onStreamDisconnected); + this.stream.on("_disconnected_", this.onStreamDisconnected); } @autobind @@ -242,7 +281,7 @@ class Pool { public connect(): void { if (this.isConnected) return; this.isConnected = true; - this.stream.send('connect', { + this.stream.send("connect", { channel: this.channel, id: this.id, }); @@ -250,13 +289,15 @@ class Pool { @autobind private disconnect(): void { - this.stream.off('_disconnected_', this.onStreamDisconnected); - this.stream.send('disconnect', { id: this.id }); + this.stream.off("_disconnected_", this.onStreamDisconnected); + this.stream.send("disconnect", { id: this.id }); this.stream.removeSharedConnectionPool(this); } } -export abstract class Connection = any> extends EventEmitter { +export abstract class Connection< + Channel extends AnyOf = any, +> extends EventEmitter { public channel: string; protected stream: Stream; public abstract id: string; @@ -274,8 +315,11 @@ export abstract class Connection = any> extends } @autobind - public send(type: T, body: Channel['receives'][T]): void { - this.stream.send('ch', { + public send( + type: T, + body: Channel["receives"][T], + ): void { + this.stream.send("ch", { id: this.id, type: type, body: body, @@ -287,7 +331,9 @@ export abstract class Connection = any> extends public abstract dispose(): void; } -class SharedConnection = any> extends Connection { +class SharedConnection< + Channel extends AnyOf = any, +> extends Connection { private pool: Pool; public get id(): string { @@ -309,11 +355,18 @@ class SharedConnection = any> extends Connection } } -class NonSharedConnection = any> extends Connection { +class NonSharedConnection< + Channel extends AnyOf = any, +> extends Connection { public id: string; - protected params: Channel['params']; + protected params: Channel["params"]; - constructor(stream: Stream, channel: string, id: string, params: Channel['params']) { + constructor( + stream: Stream, + channel: string, + id: string, + params: Channel["params"], + ) { super(stream, channel); this.params = params; @@ -324,7 +377,7 @@ class NonSharedConnection = any> extends Connect @autobind public connect(): void { - this.stream.send('connect', { + this.stream.send("connect", { channel: this.channel, id: this.id, params: this.params, @@ -334,7 +387,7 @@ class NonSharedConnection = any> extends Connect @autobind public dispose(): void { this.removeAllListeners(); - this.stream.send('disconnect', { id: this.id }); + this.stream.send("disconnect", { id: this.id }); this.stream.disconnectToChannel(this); } } diff --git a/packages/calckey-js/src/streaming.types.ts b/packages/calckey-js/src/streaming.types.ts index 24adb9e3c0..44ef647bcc 100644 --- a/packages/calckey-js/src/streaming.types.ts +++ b/packages/calckey-js/src/streaming.types.ts @@ -1,4 +1,15 @@ -import { Antenna, CustomEmoji, DriveFile, MeDetailed, MessagingMessage, Note, Notification, PageEvent, User, UserGroup } from './entities'; +import { + Antenna, + CustomEmoji, + DriveFile, + MeDetailed, + MessagingMessage, + Note, + Notification, + PageEvent, + User, + UserGroup, +} from "./entities"; type FIXME = any; @@ -15,12 +26,12 @@ export type Channels = { unfollow: (payload: User) => void; // 自分が他人をフォロー解除したとき meUpdated: (payload: MeDetailed) => void; pageEvent: (payload: PageEvent) => void; - urlUploadFinished: (payload: { marker: string; file: DriveFile; }) => void; + urlUploadFinished: (payload: { marker: string; file: DriveFile }) => void; readAllNotifications: () => void; unreadNotification: (payload: Notification) => void; - unreadMention: (payload: Note['id']) => void; + unreadMention: (payload: Note["id"]) => void; readAllUnreadMentions: () => void; - unreadSpecifiedNote: (payload: Note['id']) => void; + unreadSpecifiedNote: (payload: Note["id"]) => void; readAllUnreadSpecifiedNotes: () => void; readAllMessagingMessages: () => void; messagingMessage: (payload: MessagingMessage) => void; @@ -29,7 +40,7 @@ export type Channels = { unreadAntenna: (payload: Antenna) => void; readAllAnnouncements: () => void; readAllChannels: () => void; - unreadChannel: (payload: Note['id']) => void; + unreadChannel: (payload: Note["id"]) => void; myTokenRegenerated: () => void; reversiNoInvites: () => void; reversiInvited: (payload: FIXME) => void; @@ -81,18 +92,18 @@ export type Channels = { }; messaging: { params: { - otherparty?: User['id'] | null; - group?: UserGroup['id'] | null; + otherparty?: User["id"] | null; + group?: UserGroup["id"] | null; }; events: { message: (payload: MessagingMessage) => void; - deleted: (payload: MessagingMessage['id']) => void; - read: (payload: MessagingMessage['id'][]) => void; + deleted: (payload: MessagingMessage["id"]) => void; + read: (payload: MessagingMessage["id"][]) => void; typers: (payload: User[]) => void; }; receives: { read: { - id: MessagingMessage['id']; + id: MessagingMessage["id"]; }; }; }; @@ -122,40 +133,45 @@ export type Channels = { }; }; -export type NoteUpdatedEvent = { - id: Note['id']; - type: 'reacted'; - body: { - reaction: string; - userId: User['id']; - }; -} | { - id: Note['id']; - type: 'unreacted'; - body: { - reaction: string; - userId: User['id']; - }; -} | { - id: Note['id']; - type: 'deleted'; - body: { - deletedAt: string; - }; -} | { - id: Note['id']; - type: 'pollVoted'; - body: { - choice: number; - userId: User['id']; - }; -} | { - id: Note['id']; - type: 'replied'; - body: { - id: Note['id']; - }; -}; +export type NoteUpdatedEvent = + | { + id: Note["id"]; + type: "reacted"; + body: { + reaction: string; + userId: User["id"]; + }; + } + | { + id: Note["id"]; + type: "unreacted"; + body: { + reaction: string; + userId: User["id"]; + }; + } + | { + id: Note["id"]; + type: "deleted"; + body: { + deletedAt: string; + }; + } + | { + id: Note["id"]; + type: "pollVoted"; + body: { + choice: number; + userId: User["id"]; + }; + } + | { + id: Note["id"]; + type: "replied"; + body: { + id: Note["id"]; + }; + }; export type BroadcastEvents = { noteUpdated: (payload: NoteUpdatedEvent) => void; diff --git a/packages/calckey-js/test-d/api.ts b/packages/calckey-js/test-d/api.ts index ce793f6fd1..c5018177c9 100644 --- a/packages/calckey-js/test-d/api.ts +++ b/packages/calckey-js/test-d/api.ts @@ -1,45 +1,48 @@ -import { expectType } from 'tsd'; -import * as Misskey from '../src'; +import { expectType } from "tsd"; +import * as Misskey from "../src"; -describe('API', () => { - test('success', async () => { +describe("API", () => { + test("success", async () => { const cli = new Misskey.api.APIClient({ - origin: 'https://misskey.test', - credential: 'TOKEN' + origin: "https://misskey.test", + credential: "TOKEN", }); - const res = await cli.request('meta', { detail: true }); + const res = await cli.request("meta", { detail: true }); expectType(res); }); - test('conditional respose type (meta)', async () => { + test("conditional respose type (meta)", async () => { const cli = new Misskey.api.APIClient({ - origin: 'https://misskey.test', - credential: 'TOKEN' + origin: "https://misskey.test", + credential: "TOKEN", }); - const res = await cli.request('meta', { detail: true }); + const res = await cli.request("meta", { detail: true }); expectType(res); - const res2 = await cli.request('meta', { detail: false }); + const res2 = await cli.request("meta", { detail: false }); expectType(res2); - const res3 = await cli.request('meta', { }); + const res3 = await cli.request("meta", {}); expectType(res3); - const res4 = await cli.request('meta', { detail: true as boolean }); - expectType(res4); + const res4 = await cli.request("meta", { detail: true as boolean }); + expectType< + | Misskey.entities.LiteInstanceMetadata + | Misskey.entities.DetailedInstanceMetadata + >(res4); }); - test('conditional respose type (users/show)', async () => { + test("conditional respose type (users/show)", async () => { const cli = new Misskey.api.APIClient({ - origin: 'https://misskey.test', - credential: 'TOKEN' + origin: "https://misskey.test", + credential: "TOKEN", }); - const res = await cli.request('users/show', { userId: 'xxxxxxxx' }); + const res = await cli.request("users/show", { userId: "xxxxxxxx" }); expectType(res); - const res2 = await cli.request('users/show', { userIds: ['xxxxxxxx'] }); + const res2 = await cli.request("users/show", { userIds: ["xxxxxxxx"] }); expectType(res2); }); }); diff --git a/packages/calckey-js/test-d/streaming.ts b/packages/calckey-js/test-d/streaming.ts index 7ff7f95999..c49795dcc2 100644 --- a/packages/calckey-js/test-d/streaming.ts +++ b/packages/calckey-js/test-d/streaming.ts @@ -1,25 +1,31 @@ -import { expectType } from 'tsd'; -import * as Misskey from '../src'; +import { expectType } from "tsd"; +import * as Misskey from "../src"; -describe('Streaming', () => { - test('emit type', async () => { - const stream = new Misskey.Stream('https://misskey.test', { token: 'TOKEN' }); - const mainChannel = stream.useChannel('main'); - mainChannel.on('notification', notification => { +describe("Streaming", () => { + test("emit type", async () => { + const stream = new Misskey.Stream("https://misskey.test", { + token: "TOKEN", + }); + const mainChannel = stream.useChannel("main"); + mainChannel.on("notification", (notification) => { expectType(notification); }); }); - test('params type', async () => { - const stream = new Misskey.Stream('https://misskey.test', { token: 'TOKEN' }); + test("params type", async () => { + const stream = new Misskey.Stream("https://misskey.test", { + token: "TOKEN", + }); // TODO: 「stream.useChannel の第二引数として受け入れる型が // { // otherparty?: User['id'] | null; // group?: UserGroup['id'] | null; - // } + // } // になっている」というテストを行いたいけどtsdでの書き方がわからない - const messagingChannel = stream.useChannel('messaging', { otherparty: 'aaa' }); - messagingChannel.on('message', message => { + const messagingChannel = stream.useChannel("messaging", { + otherparty: "aaa", + }); + messagingChannel.on("message", (message) => { expectType(message); }); }); diff --git a/packages/calckey-js/test/api.ts b/packages/calckey-js/test/api.ts index 47c8378014..a8a1fc0e6f 100644 --- a/packages/calckey-js/test/api.ts +++ b/packages/calckey-js/test/api.ts @@ -1,28 +1,28 @@ -import { APIClient, isAPIError } from '../src/api'; -import { enableFetchMocks } from 'jest-fetch-mock'; +import { APIClient, isAPIError } from "../src/api"; +import { enableFetchMocks } from "jest-fetch-mock"; enableFetchMocks(); function getFetchCall(call: any[]) { const { body, method } = call[1]; - if (body != null && typeof body != 'string') { - throw new Error('invalid body'); + if (body != null && typeof body != "string") { + throw new Error("invalid body"); } return { url: call[0], method: method, - body: JSON.parse(body as any) + body: JSON.parse(body as any), }; } -describe('API', () => { - test('success', async () => { +describe("API", () => { + test("success", async () => { fetchMock.resetMocks(); fetchMock.mockResponse(async (req) => { const body = await req.json(); - if (req.method == 'POST' && req.url == 'https://misskey.test/api/i') { - if (body.i === 'TOKEN') { - return JSON.stringify({ id: 'foo' }); + if (req.method == "POST" && req.url == "https://misskey.test/api/i") { + if (body.i === "TOKEN") { + return JSON.stringify({ id: "foo" }); } else { return { status: 400 }; } @@ -32,30 +32,33 @@ describe('API', () => { }); const cli = new APIClient({ - origin: 'https://misskey.test', - credential: 'TOKEN', + origin: "https://misskey.test", + credential: "TOKEN", }); - const res = await cli.request('i'); + const res = await cli.request("i"); expect(res).toEqual({ - id: 'foo' + id: "foo", }); expect(getFetchCall(fetchMock.mock.calls[0])).toEqual({ - url: 'https://misskey.test/api/i', - method: 'POST', - body: { i: 'TOKEN' } + url: "https://misskey.test/api/i", + method: "POST", + body: { i: "TOKEN" }, }); }); - test('with params', async () => { + test("with params", async () => { fetchMock.resetMocks(); fetchMock.mockResponse(async (req) => { const body = await req.json(); - if (req.method == 'POST' && req.url == 'https://misskey.test/api/notes/show') { - if (body.i === 'TOKEN' && body.noteId === 'aaaaa') { - return JSON.stringify({ id: 'foo' }); + if ( + req.method == "POST" && + req.url == "https://misskey.test/api/notes/show" + ) { + if (body.i === "TOKEN" && body.noteId === "aaaaa") { + return JSON.stringify({ id: "foo" }); } else { return { status: 400 }; } @@ -65,27 +68,30 @@ describe('API', () => { }); const cli = new APIClient({ - origin: 'https://misskey.test', - credential: 'TOKEN', + origin: "https://misskey.test", + credential: "TOKEN", }); - const res = await cli.request('notes/show', { noteId: 'aaaaa' }); + const res = await cli.request("notes/show", { noteId: "aaaaa" }); expect(res).toEqual({ - id: 'foo' + id: "foo", }); expect(getFetchCall(fetchMock.mock.calls[0])).toEqual({ - url: 'https://misskey.test/api/notes/show', - method: 'POST', - body: { i: 'TOKEN', noteId: 'aaaaa' } + url: "https://misskey.test/api/notes/show", + method: "POST", + body: { i: "TOKEN", noteId: "aaaaa" }, }); }); - test('204 No Content で null が返る', async () => { + test("204 No Content で null が返る", async () => { fetchMock.resetMocks(); fetchMock.mockResponse(async (req) => { - if (req.method == 'POST' && req.url == 'https://misskey.test/api/reset-password') { + if ( + req.method == "POST" && + req.url == "https://misskey.test/api/reset-password" + ) { return { status: 204 }; } else { return { status: 404 }; @@ -93,38 +99,41 @@ describe('API', () => { }); const cli = new APIClient({ - origin: 'https://misskey.test', - credential: 'TOKEN', + origin: "https://misskey.test", + credential: "TOKEN", }); - const res = await cli.request('reset-password', { token: 'aaa', password: 'aaa' }); + const res = await cli.request("reset-password", { + token: "aaa", + password: "aaa", + }); expect(res).toEqual(null); expect(getFetchCall(fetchMock.mock.calls[0])).toEqual({ - url: 'https://misskey.test/api/reset-password', - method: 'POST', - body: { i: 'TOKEN', token: 'aaa', password: 'aaa' } + url: "https://misskey.test/api/reset-password", + method: "POST", + body: { i: "TOKEN", token: "aaa", password: "aaa" }, }); }); - test('インスタンスの credential が指定されていても引数で credential が null ならば null としてリクエストされる', async () => { + test("インスタンスの credential が指定されていても引数で credential が null ならば null としてリクエストされる", async () => { fetchMock.resetMocks(); fetchMock.mockResponse(async (req) => { const body = await req.json(); - if (req.method == 'POST' && req.url == 'https://misskey.test/api/i') { - if (typeof body.i === 'string') { - return JSON.stringify({ id: 'foo' }); + if (req.method == "POST" && req.url == "https://misskey.test/api/i") { + if (typeof body.i === "string") { + return JSON.stringify({ id: "foo" }); } else { return { status: 401, body: JSON.stringify({ error: { - message: 'Credential required.', - code: 'CREDENTIAL_REQUIRED', - id: '1384574d-a912-4b81-8601-c7b1c4085df1', - } - }) + message: "Credential required.", + code: "CREDENTIAL_REQUIRED", + id: "1384574d-a912-4b81-8601-c7b1c4085df1", + }, + }), }; } } else { @@ -134,77 +143,78 @@ describe('API', () => { try { const cli = new APIClient({ - origin: 'https://misskey.test', - credential: 'TOKEN', + origin: "https://misskey.test", + credential: "TOKEN", }); - - await cli.request('i', {}, null); + + await cli.request("i", {}, null); } catch (e) { expect(isAPIError(e)).toEqual(true); } }); - test('api error', async () => { + test("api error", async () => { fetchMock.resetMocks(); fetchMock.mockResponse(async (req) => { return { status: 500, body: JSON.stringify({ error: { - message: 'Internal error occurred. Please contact us if the error persists.', - code: 'INTERNAL_ERROR', - id: '5d37dbcb-891e-41ca-a3d6-e690c97775ac', - kind: 'server', + message: + "Internal error occurred. Please contact us if the error persists.", + code: "INTERNAL_ERROR", + id: "5d37dbcb-891e-41ca-a3d6-e690c97775ac", + kind: "server", }, - }) + }), }; }); try { const cli = new APIClient({ - origin: 'https://misskey.test', - credential: 'TOKEN', + origin: "https://misskey.test", + credential: "TOKEN", }); - - await cli.request('i'); + + await cli.request("i"); } catch (e: any) { expect(isAPIError(e)).toEqual(true); - expect(e.id).toEqual('5d37dbcb-891e-41ca-a3d6-e690c97775ac'); + expect(e.id).toEqual("5d37dbcb-891e-41ca-a3d6-e690c97775ac"); } }); - test('network error', async () => { + test("network error", async () => { fetchMock.resetMocks(); fetchMock.mockAbort(); try { const cli = new APIClient({ - origin: 'https://misskey.test', - credential: 'TOKEN', + origin: "https://misskey.test", + credential: "TOKEN", }); - - await cli.request('i'); + + await cli.request("i"); } catch (e) { expect(isAPIError(e)).toEqual(false); } }); - test('json parse error', async () => { + test("json parse error", async () => { fetchMock.resetMocks(); fetchMock.mockResponse(async (req) => { return { status: 500, - body: 'I AM NOT JSON' + body: "I AM NOT JSON", }; }); try { const cli = new APIClient({ - origin: 'https://misskey.test', - credential: 'TOKEN', + origin: "https://misskey.test", + credential: "TOKEN", }); - - await cli.request('i'); + + await cli.request("i"); } catch (e) { expect(isAPIError(e)).toEqual(false); } diff --git a/packages/calckey-js/test/streaming.ts b/packages/calckey-js/test/streaming.ts index 913db8b287..920a9102b8 100644 --- a/packages/calckey-js/test/streaming.ts +++ b/packages/calckey-js/test/streaming.ts @@ -1,95 +1,105 @@ -import WS from 'jest-websocket-mock'; -import Stream from '../src/streaming'; +import WS from "jest-websocket-mock"; +import Stream from "../src/streaming"; -describe('Streaming', () => { - test('useChannel', async () => { - const server = new WS('wss://misskey.test/streaming'); - const stream = new Stream('https://misskey.test', { token: 'TOKEN' }); +describe("Streaming", () => { + test("useChannel", async () => { + const server = new WS("wss://misskey.test/streaming"); + const stream = new Stream("https://misskey.test", { token: "TOKEN" }); const mainChannelReceived: any[] = []; - const main = stream.useChannel('main'); - main.on('meUpdated', payload => { + const main = stream.useChannel("main"); + main.on("meUpdated", (payload) => { mainChannelReceived.push(payload); }); const ws = await server.connected; - expect(new URLSearchParams(new URL(ws.url).search).get('i')).toEqual('TOKEN'); + expect(new URLSearchParams(new URL(ws.url).search).get("i")).toEqual( + "TOKEN", + ); - const msg = JSON.parse(await server.nextMessage as string); + const msg = JSON.parse((await server.nextMessage) as string); const mainChannelId = msg.body.id; - expect(msg.type).toEqual('connect'); - expect(msg.body.channel).toEqual('main'); + expect(msg.type).toEqual("connect"); + expect(msg.body.channel).toEqual("main"); expect(mainChannelId != null).toEqual(true); - server.send(JSON.stringify({ - type: 'channel', - body: { - id: mainChannelId, - type: 'meUpdated', + server.send( + JSON.stringify({ + type: "channel", body: { - id: 'foo' - } - } - })); + id: mainChannelId, + type: "meUpdated", + body: { + id: "foo", + }, + }, + }), + ); expect(mainChannelReceived[0]).toEqual({ - id: 'foo' + id: "foo", }); stream.close(); server.close(); }); - test('useChannel with parameters', async () => { - const server = new WS('wss://misskey.test/streaming'); - const stream = new Stream('https://misskey.test', { token: 'TOKEN' }); + test("useChannel with parameters", async () => { + const server = new WS("wss://misskey.test/streaming"); + const stream = new Stream("https://misskey.test", { token: "TOKEN" }); const messagingChannelReceived: any[] = []; - const messaging = stream.useChannel('messaging', { otherparty: 'aaa' }); - messaging.on('message', payload => { + const messaging = stream.useChannel("messaging", { otherparty: "aaa" }); + messaging.on("message", (payload) => { messagingChannelReceived.push(payload); }); const ws = await server.connected; - expect(new URLSearchParams(new URL(ws.url).search).get('i')).toEqual('TOKEN'); + expect(new URLSearchParams(new URL(ws.url).search).get("i")).toEqual( + "TOKEN", + ); - const msg = JSON.parse(await server.nextMessage as string); + const msg = JSON.parse((await server.nextMessage) as string); const messagingChannelId = msg.body.id; - expect(msg.type).toEqual('connect'); - expect(msg.body.channel).toEqual('messaging'); - expect(msg.body.params).toEqual({ otherparty: 'aaa' }); + expect(msg.type).toEqual("connect"); + expect(msg.body.channel).toEqual("messaging"); + expect(msg.body.params).toEqual({ otherparty: "aaa" }); expect(messagingChannelId != null).toEqual(true); - server.send(JSON.stringify({ - type: 'channel', - body: { - id: messagingChannelId, - type: 'message', + server.send( + JSON.stringify({ + type: "channel", body: { - id: 'foo' - } - } - })); + id: messagingChannelId, + type: "message", + body: { + id: "foo", + }, + }, + }), + ); expect(messagingChannelReceived[0]).toEqual({ - id: 'foo' + id: "foo", }); stream.close(); server.close(); }); - test('ちゃんとチャンネルごとにidが異なる', async () => { - const server = new WS('wss://misskey.test/streaming'); - const stream = new Stream('https://misskey.test', { token: 'TOKEN' }); + test("ちゃんとチャンネルごとにidが異なる", async () => { + const server = new WS("wss://misskey.test/streaming"); + const stream = new Stream("https://misskey.test", { token: "TOKEN" }); - stream.useChannel('messaging', { otherparty: 'aaa' }); - stream.useChannel('messaging', { otherparty: 'bbb' }); + stream.useChannel("messaging", { otherparty: "aaa" }); + stream.useChannel("messaging", { otherparty: "bbb" }); const ws = await server.connected; - expect(new URLSearchParams(new URL(ws.url).search).get('i')).toEqual('TOKEN'); + expect(new URLSearchParams(new URL(ws.url).search).get("i")).toEqual( + "TOKEN", + ); - const msg = JSON.parse(await server.nextMessage as string); + const msg = JSON.parse((await server.nextMessage) as string); const messagingChannelId = msg.body.id; - const msg2 = JSON.parse(await server.nextMessage as string); + const msg2 = JSON.parse((await server.nextMessage) as string); const messagingChannelId2 = msg2.body.id; expect(messagingChannelId != null).toEqual(true); @@ -100,58 +110,64 @@ describe('Streaming', () => { server.close(); }); - test('Connection#send', async () => { - const server = new WS('wss://misskey.test/streaming'); - const stream = new Stream('https://misskey.test', { token: 'TOKEN' }); + test("Connection#send", async () => { + const server = new WS("wss://misskey.test/streaming"); + const stream = new Stream("https://misskey.test", { token: "TOKEN" }); - const messaging = stream.useChannel('messaging', { otherparty: 'aaa' }); - messaging.send('read', { id: 'aaa' }); + const messaging = stream.useChannel("messaging", { otherparty: "aaa" }); + messaging.send("read", { id: "aaa" }); const ws = await server.connected; - expect(new URLSearchParams(new URL(ws.url).search).get('i')).toEqual('TOKEN'); + expect(new URLSearchParams(new URL(ws.url).search).get("i")).toEqual( + "TOKEN", + ); - const connectMsg = JSON.parse(await server.nextMessage as string); + const connectMsg = JSON.parse((await server.nextMessage) as string); const channelId = connectMsg.body.id; - const msg = JSON.parse(await server.nextMessage as string); + const msg = JSON.parse((await server.nextMessage) as string); - expect(msg.type).toEqual('ch'); + expect(msg.type).toEqual("ch"); expect(msg.body.id).toEqual(channelId); - expect(msg.body.type).toEqual('read'); - expect(msg.body.body).toEqual({ id: 'aaa' }); + expect(msg.body.type).toEqual("read"); + expect(msg.body.body).toEqual({ id: "aaa" }); stream.close(); server.close(); }); - test('Connection#dispose', async () => { - const server = new WS('wss://misskey.test/streaming'); - const stream = new Stream('https://misskey.test', { token: 'TOKEN' }); + test("Connection#dispose", async () => { + const server = new WS("wss://misskey.test/streaming"); + const stream = new Stream("https://misskey.test", { token: "TOKEN" }); const mainChannelReceived: any[] = []; - const main = stream.useChannel('main'); - main.on('meUpdated', payload => { + const main = stream.useChannel("main"); + main.on("meUpdated", (payload) => { mainChannelReceived.push(payload); }); - - const ws = await server.connected; - expect(new URLSearchParams(new URL(ws.url).search).get('i')).toEqual('TOKEN'); - const msg = JSON.parse(await server.nextMessage as string); + const ws = await server.connected; + expect(new URLSearchParams(new URL(ws.url).search).get("i")).toEqual( + "TOKEN", + ); + + const msg = JSON.parse((await server.nextMessage) as string); const mainChannelId = msg.body.id; - expect(msg.type).toEqual('connect'); - expect(msg.body.channel).toEqual('main'); + expect(msg.type).toEqual("connect"); + expect(msg.body.channel).toEqual("main"); expect(mainChannelId != null).toEqual(true); main.dispose(); - server.send(JSON.stringify({ - type: 'channel', - body: { - id: mainChannelId, - type: 'meUpdated', + server.send( + JSON.stringify({ + type: "channel", body: { - id: 'foo' - } - } - })); + id: mainChannelId, + type: "meUpdated", + body: { + id: "foo", + }, + }, + }), + ); expect(mainChannelReceived.length).toEqual(0); diff --git a/packages/client/src/store.ts b/packages/client/src/store.ts index 15cb69ff04..2aa204d18d 100644 --- a/packages/client/src/store.ts +++ b/packages/client/src/store.ts @@ -1,7 +1,7 @@ import { markRaw, ref } from "vue"; import { Storage } from "./pizzax"; import { Theme } from "./scripts/theme"; -import { deviceKind } from '@/scripts/device-kind'; +import { deviceKind } from "@/scripts/device-kind"; export const postFormActions = []; export const userActions = []; @@ -16,10 +16,10 @@ const menuOptions = [ "explore", "favorites", "channels", - "search" + "search", ]; -if (deviceKind === 'desktop') menuOptions.push("ui"); +if (deviceKind === "desktop") menuOptions.push("ui"); // TODO: それぞれいちいちwhereとかdefaultというキーを付けなきゃいけないの冗長なのでなんとかする(ただ型定義が面倒になりそう) // あと、現行の定義の仕方なら「whereが何であるかに関わらずキー名の重複不可」という制約を付けられるメリットもあるからそのメリットを引き継ぐ方法も考えないといけない