Merge remote-branch 'misskey/develop'

This commit is contained in:
NoriDev 2023-12-05 17:10:53 +09:00
commit f25a1b50c8
100 changed files with 30870 additions and 4758 deletions

View file

@ -6,6 +6,7 @@
### Client
- Fix: ページ一覧ページの表示がモバイル環境において崩れているのを修正
- Fix: MFMでルビの中のテキストがnyaizeされない問題を修正
### Server
-
@ -22,6 +23,8 @@
### Client
- Feat: 今日誕生日のフォロー中のユーザーを一覧表示できるウィジェットを追加
- Feat: データセーバーでコードハイライトの読み込みを削減できるように
- Enhance: 投稿フォームの絵文字ピッカーをリアクション時に使用するものと同じのを使用するように #12336
- Enhance: 絵文字のオートコンプリート機能強化 #12364
- Enhance: ユーザーのRawデータを表示するページが復活
- Enhance: リアクション選択時に音を鳴らせるように
@ -30,6 +33,8 @@
- Enhance: Shareページで投稿を完了すると、親ウィンドウ親フレームにpostMessageするように
- Enhance: チャンネル、クリップ、ページ、Play、ギャラリーにURLのコピーボタンを設置 #11305
- Enhance: ノートプレビューに「内容を隠す」が反映されるように
- Enhance: データセーバーの適用範囲を個別で設定できるように
- 従来のデータセーバーの設定はリセットされます
- fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
- Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
- Enhance: 絵文字の詳細ページに記載される情報を追加
@ -38,6 +43,7 @@
- Fix: 一度に大量の通知が入った際に通知音が音割れする問題を修正
- Fix: 共有機能をサポートしていないブラウザの場合は共有ボタンを非表示にする #11305
- Fix: 通知のグルーピング設定を変更してもリロードされるまで表示が変わらない問題を修正 #12470
- Fix: 長い名前のチャンネルにおける投稿フォームの表示が崩れる問題を修正
### Server
- Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
@ -47,6 +53,11 @@
- Fix: 招待コードが使い回せる問題を修正
- Fix: 特定の条件下でチャンネルやユーザーのノート一覧に最新のノートが表示されなくなる問題を修正
- Fix: 何もノートしていないユーザーのフィードにアクセスするとエラーになる問題を修正
- Fix: リストタイムラインにてミュートが機能しないケースがある問題と、チャンネル投稿がストリーミングで流れてきてしまう問題を修正 #10443
- Fix: 「みつける」のなかにミュートしたユーザが現れてしまう問題を修正 #12383
- Fix: Social/Local/Home Timelineにてインスタンスミュートが効かない問題
- Fix: ユーザのノート一覧にてインスタンスミュートが効かない問題
- Fix: チャンネルのノート一覧にてインスタンスミュートが効かない問題
## 2023.11.1

View file

@ -2600,6 +2600,19 @@ _externalResourceInstaller:
_themeInstallFailed:
title: "Failed to install theme"
description: "A problem occurred during theme installation. Please try again. Error details can be viewed in the Javascript console."
_dataSaver:
_media:
title: "Load media"
description: "Prevents images/videos from being loaded automatically. Hidden images/videos will be loaded when tapped."
_avatar:
title: "User icon image"
description: "Animation of user's icon images stops. Since animated images can be larger in file size than normal images, data traffic can be further reduced."
_urlPreview:
title: "URL preview thumbnail"
description: "URL preview thumbnail images will no longer load."
_code:
title: "Code Highlights"
description: "If code highlighting notation is used, such as MFM, it will not be loaded until tapped. Code highlighting requires loading the definition file for each language to be highlighted, but since these files are no longer loaded automatically, a reduction in communication volume can be expected."
_abuse:
_resolver:
1hour: "one hour"
@ -2624,16 +2637,3 @@ _imageCompressionMode:
noResizeCompress: "Compression without resize"
resizeCompressLossy: "Resize and lossy compression"
noResizeCompressLossy: "Lossy compression without resize"
_dataSaver:
_media:
title: "Load media"
description: "Prevents images/videos from being loaded automatically. Hidden images/videos will be loaded when tapped."
_avatar:
title: "User icon image"
description: "Animation of user's icon images stops. Since animated images can be larger in file size than normal images, data traffic can be further reduced."
_urlPreview:
title: "URL preview thumbnail"
description: "URL preview thumbnail images will no longer load."
_code:
title: "Code Highlights"
description: "If code highlighting notation is used, such as MFM, it will not be loaded until tapped. Code highlighting requires loading the definition file for each language to be highlighted, but since these files are no longer loaded automatically, a reduction in communication volume can be expected."

38
locales/index.d.ts vendored
View file

@ -1277,7 +1277,7 @@ export interface Locale {
"cwNotationRequired": string;
"doReaction": string;
"code": string;
"tryReloadIfNotApplied": string;
"reloadRequiredToApplySettings": string;
"showUnreadNotificationsCount": string;
"showCatOnly": string;
"additionalPermissionsForFlash": string;
@ -2824,6 +2824,24 @@ export interface Locale {
};
};
};
"_dataSaver": {
"_media": {
"title": string;
"description": string;
};
"_avatar": {
"title": string;
"description": string;
};
"_urlPreview": {
"title": string;
"description": string;
};
"_code": {
"title": string;
"description": string;
};
};
"_abuse": {
"_resolver": {
"1hour": string;
@ -2851,24 +2869,6 @@ export interface Locale {
"resizeCompressLossy": string;
"noResizeCompressLossy": string;
};
"_dataSaver": {
"_media": {
"title": string;
"description": string;
};
"_avatar": {
"title": string;
"description": string;
};
"_urlPreview": {
"title": string;
"description": string;
};
"_code": {
"title": string;
"description": string;
};
};
}
declare const locales: {
[lang: string]: Locale;

View file

@ -1274,7 +1274,7 @@ signupPendingError: "メールアドレスの確認中に問題が発生しま
cwNotationRequired: "「内容を隠す」がオンの場合は注釈の記述が必要です。"
doReaction: "リアクションする"
code: "コード"
tryReloadIfNotApplied: "設定が反映されない場合はリロードをお試しください。"
reloadRequiredToApplySettings: "設定の反映にはリロードが必要です。"
showUnreadNotificationsCount: "未読の通知の数を表示する"
showCatOnly: "キャット付きのみ"
additionalPermissionsForFlash: "Playへの追加許可"
@ -2709,6 +2709,20 @@ _externalResourceInstaller:
title: "テーマのインストールに失敗しました"
description: "テーマのインストール中に問題が発生しました。もう一度お試しください。エラーの詳細はJavascriptコンソールをご覧ください。"
_dataSaver:
_media:
title: "メディアの読み込み"
description: "画像・動画が自動で読み込まれるのを防止します。隠れている画像・動画はタップすると読み込まれます。"
_avatar:
title: "アイコン画像"
description: "アイコン画像のアニメーションが停止します。アニメーション画像は通常の画像よりファイルサイズが大きいことがあるので、データ通信量をさらに削減できます。"
_urlPreview:
title: "URLプレビューのサムネイル"
description: "URLプレビューのサムネイル画像が読み込まれなくなります。"
_code:
title: "コードハイライト"
description: "MFMなどでコードハイライト記法が使われている場合、タップするまで読み込まれなくなります。コードハイライトではハイライトする言語ごとにその定義ファイルを読み込む必要がありますが、それらが自動で読み込まれなくなるため、通信量の削減が見込めます。"
_abuse:
_resolver:
1hour: "一時間"
@ -2734,17 +2748,3 @@ _imageCompressionMode:
noResizeCompress: "縮小せず再圧縮する"
resizeCompressLossy: "縮小して非可逆圧縮する"
noResizeCompressLossy: "縮小せず非可逆圧縮する"
_dataSaver:
_media:
title: "メディアの読み込み"
description: "画像・動画が自動で読み込まれるのを防止します。隠れている画像・動画はタップすると読み込まれます。"
_avatar:
title: "アイコン画像"
description: "アイコン画像のアニメーションが停止します。アニメーション画像は通常の画像よりファイルサイズが大きいことがあるので、データ通信量をさらに削減できます。"
_urlPreview:
title: "URLプレビューのサムネイル"
description: "URLプレビューのサムネイル画像が読み込まれなくなります。"
_code:
title: "コードハイライト"
description: "MFMなどでコードハイライト記法が使われている場合、タップするまで読み込まれなくなります。コードハイライトではハイライトする言語ごとにその定義ファイルを読み込む必要がありますが、それらが自動で読み込まれなくなるため、通信量の削減が見込めます。"

View file

@ -1264,7 +1264,7 @@ signupPendingError: "메일 주소 확인중에 문제가 발생했어요. 링
cwNotationRequired: "'내용 숨기기'를 체크했을 경우 주석을 작성해야 해요."
doReaction: "리액션 추가"
code: "코드"
tryReloadIfNotApplied: "설정이 반영되지 않으면 새로 고침을 시도해 보세요."
tryReloadIfNotApplied: "설정을 반영하려면 페이지를 다시 불러와야 해요."
showUnreadNotificationsCount: "읽지 않은 알림 수 표시"
showCatOnly: "고양이만 보기"
additionalPermissionsForFlash: "Play에 대한 추가 권한"
@ -2597,6 +2597,19 @@ _externalResourceInstaller:
_themeInstallFailed:
title: "테마 설치에 실패했어요"
description: "테마를 설치하는 동안 문제가 발생했어요. 다시 시도해 주세요. 오류에 대한 자세한 내용은 자바스크립트 콘솔을 참고해 주세요."
_dataSaver:
_media:
title: "미디어 불러오기 방지"
description: "이미지/동영상이 자동으로 로드되는 것을 방지해요. 탭하면 숨겨진 이미지/동영상을 불러올 수 있어요."
_avatar:
title: "움직이는 프로필 아이콘"
description: "움직이는 프로필 아이콘이 정적 이미지로 변경되어 표시돼요. 움직이는 이미지는 일반 이미지보다 파일 크기가 클 수 있기 때문에 데이터 통신량을 더욱 줄일 수 있어요."
_urlPreview:
title: "URL 미리보기 썸네일"
description: "URL 미리보기의 썸네일 이미지를 불러오지 않아요."
_code:
title: "코드 하이라이트"
description: "MFM 등에서 코드 하이라이트 기법을 사용하는 경우, 탭하기 전까지는 코드 하이라이트를 불러오지 않아요. 코드 하이라이트는 강조할 언어마다 해당 정의 파일을 불러와야 하지만, 이를 자동으로 불러오지 않기 때문에 통신량 감소를 기대할 수 있어요."
_abuse:
_resolver:
1hour: "1시간"
@ -2621,16 +2634,3 @@ _imageCompressionMode:
noResizeCompress: "해상도를 축소하지 않고 압축"
resizeCompressLossy: "해상도 축소 및 손실 압축"
noResizeCompressLossy: "해상도를 축소하지 않고 손실 압축"
_dataSaver:
_media:
title: "미디어 불러오기 방지"
description: "이미지/동영상이 자동으로 로드되는 것을 방지해요. 탭하면 숨겨진 이미지/동영상을 불러올 수 있어요."
_avatar:
title: "움직이는 프로필 아이콘"
description: "움직이는 프로필 아이콘이 정적 이미지로 변경되어 표시돼요. 움직이는 이미지는 일반 이미지보다 파일 크기가 클 수 있기 때문에 데이터 통신량을 더욱 줄일 수 있어요."
_urlPreview:
title: "URL 미리보기 썸네일"
description: "URL 미리보기의 썸네일 이미지를 불러오지 않아요."
_code:
title: "코드 하이라이트"
description: "MFM 등에서 코드 하이라이트 기법을 사용하는 경우, 탭하기 전까지는 코드 하이라이트를 불러오지 않아요. 코드 하이라이트는 강조할 언어마다 해당 정의 파일을 불러와야 하지만, 이를 자동으로 불러오지 않기 때문에 통신량 감소를 기대할 수 있어요."

View file

@ -19,6 +19,7 @@
"build-assets": "node ./scripts/build-assets.mjs",
"build": "pnpm build-pre && pnpm -r build && pnpm build-assets",
"build-storybook": "pnpm --filter frontend build-storybook",
"build-cherrypick-js-with-types": "pnpm --filter backend build && pnpm --filter backend generate-api-json && ncp packages/backend/built/api.json packages/cherrypick-js/generator/api.json && pnpm --filter cherrypick-js update-autogen-code && pnpm --filter cherrypick-js build",
"start": "pnpm check:connect && cd packages/backend && node ./built/boot/entry.js",
"start:docker": "pnpm check:connect && cd packages/backend && exec node ./built/boot/entry.js",
"start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js",
@ -61,7 +62,8 @@
"cross-env": "7.0.3",
"cypress": "13.6.0",
"eslint": "8.54.0",
"start-server-and-test": "2.0.3"
"start-server-and-test": "2.0.3",
"ncp": "2.0.0"
},
"optionalDependencies": {
"@tensorflow/tfjs-core": "4.4.0"

View file

@ -4,6 +4,7 @@
*/
import { Module } from '@nestjs/common';
import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
import { AccountMoveService } from './AccountMoveService.js';
import { AccountUpdateService } from './AccountUpdateService.js';
import { AiService } from './AiService.js';
@ -203,6 +204,7 @@ const $SearchService: Provider = { provide: 'SearchService', useExisting: Search
const $ClipService: Provider = { provide: 'ClipService', useExisting: ClipService };
const $FeaturedService: Provider = { provide: 'FeaturedService', useExisting: FeaturedService };
const $FanoutTimelineService: Provider = { provide: 'FanoutTimelineService', useExisting: FanoutTimelineService };
const $FanoutTimelineEndpointService: Provider = { provide: 'FanoutTimelineEndpointService', useExisting: FanoutTimelineEndpointService };
const $ChannelFollowingService: Provider = { provide: 'ChannelFollowingService', useExisting: ChannelFollowingService };
const $RegistryApiService: Provider = { provide: 'RegistryApiService', useExisting: RegistryApiService };
@ -345,6 +347,7 @@ const $ApEventService: Provider = { provide: 'ApEventService', useExisting: ApEv
ClipService,
FeaturedService,
FanoutTimelineService,
FanoutTimelineEndpointService,
ChannelFollowingService,
RegistryApiService,
ChartLoggerService,
@ -480,6 +483,7 @@ const $ApEventService: Provider = { provide: 'ApEventService', useExisting: ApEv
$ClipService,
$FeaturedService,
$FanoutTimelineService,
$FanoutTimelineEndpointService,
$ChannelFollowingService,
$RegistryApiService,
$ChartLoggerService,
@ -616,6 +620,7 @@ const $ApEventService: Provider = { provide: 'ApEventService', useExisting: ApEv
ClipService,
FeaturedService,
FanoutTimelineService,
FanoutTimelineEndpointService,
ChannelFollowingService,
RegistryApiService,
FederationChart,
@ -750,6 +755,7 @@ const $ApEventService: Provider = { provide: 'ApEventService', useExisting: ApEv
$ClipService,
$FeaturedService,
$FanoutTimelineService,
$FanoutTimelineEndpointService,
$ChannelFollowingService,
$RegistryApiService,
$FederationChart,

View file

@ -0,0 +1,170 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey, cherrypick contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Inject, Injectable } from '@nestjs/common';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import type { MiUser } from '@/models/User.js';
import type { MiNote } from '@/models/Note.js';
import { Packed } from '@/misc/json-schema.js';
import type { NotesRepository } from '@/models/_.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { FanoutTimelineName, FanoutTimelineService } from '@/core/FanoutTimelineService.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { isPureRenote } from '@/misc/is-pure-renote.js';
import { CacheService } from '@/core/CacheService.js';
import { isReply } from '@/misc/is-reply.js';
import { isInstanceMuted } from '@/misc/is-instance-muted.js';
type TimelineOptions = {
untilId: string | null,
sinceId: string | null,
limit: number,
allowPartial: boolean,
me?: { id: MiUser['id'] } | undefined | null,
useDbFallback: boolean,
redisTimelines: FanoutTimelineName[],
noteFilter?: (note: MiNote) => boolean,
alwaysIncludeMyNotes?: boolean;
ignoreAuthorFromMute?: boolean;
excludeNoFiles?: boolean;
excludeReplies?: boolean;
excludePureRenotes: boolean;
dbFallback: (untilId: string | null, sinceId: string | null, limit: number) => Promise<MiNote[]>,
};
@Injectable()
export class FanoutTimelineEndpointService {
constructor(
@Inject(DI.notesRepository)
private notesRepository: NotesRepository,
private noteEntityService: NoteEntityService,
private cacheService: CacheService,
private fanoutTimelineService: FanoutTimelineService,
) {
}
@bindThis
async timeline(ps: TimelineOptions): Promise<Packed<'Note'>[]> {
return await this.noteEntityService.packMany(await this.getMiNotes(ps), ps.me);
}
@bindThis
private async getMiNotes(ps: TimelineOptions): Promise<MiNote[]> {
let noteIds: string[];
let shouldFallbackToDb = false;
// 呼び出し元と以下の処理をシンプルにするためにdbFallbackを置き換える
if (!ps.useDbFallback) ps.dbFallback = () => Promise.resolve([]);
const redisResult = await this.fanoutTimelineService.getMulti(ps.redisTimelines, ps.untilId, ps.sinceId);
const redisResultIds = Array.from(new Set(redisResult.flat(1)));
redisResultIds.sort((a, b) => a > b ? -1 : 1);
noteIds = redisResultIds.slice(0, ps.limit);
shouldFallbackToDb = shouldFallbackToDb || (noteIds.length === 0);
if (!shouldFallbackToDb) {
let filter = ps.noteFilter ?? (_note => true);
if (ps.alwaysIncludeMyNotes && ps.me) {
const me = ps.me;
const parentFilter = filter;
filter = (note) => note.userId === me.id || parentFilter(note);
}
if (ps.excludeNoFiles) {
const parentFilter = filter;
filter = (note) => note.fileIds.length !== 0 && parentFilter(note);
}
if (ps.excludeReplies) {
const parentFilter = filter;
filter = (note) => !isReply(note, ps.me?.id) && parentFilter(note);
}
if (ps.excludePureRenotes) {
const parentFilter = filter;
filter = (note) => !isPureRenote(note) && parentFilter(note);
}
if (ps.me) {
const me = ps.me;
const [
userIdsWhoMeMuting,
userIdsWhoMeMutingRenotes,
userIdsWhoBlockingMe,
userMutedInstances,
] = await Promise.all([
this.cacheService.userMutingsCache.fetch(ps.me.id),
this.cacheService.renoteMutingsCache.fetch(ps.me.id),
this.cacheService.userBlockedCache.fetch(ps.me.id),
this.cacheService.userProfileCache.fetch(me.id).then(p => new Set(p.mutedInstances)),
]);
const parentFilter = filter;
filter = (note) => {
if (isUserRelated(note, userIdsWhoBlockingMe, ps.ignoreAuthorFromMute)) return false;
if (isUserRelated(note, userIdsWhoMeMuting, ps.ignoreAuthorFromMute)) return false;
if (isPureRenote(note) && isUserRelated(note, userIdsWhoMeMutingRenotes, ps.ignoreAuthorFromMute)) return false;
if (isInstanceMuted(note, userMutedInstances)) return false;
return parentFilter(note);
};
}
const redisTimeline: MiNote[] = [];
let readFromRedis = 0;
let lastSuccessfulRate = 1; // rateをキャッシュする
while ((redisResultIds.length - readFromRedis) !== 0) {
const remainingToRead = ps.limit - redisTimeline.length;
// DBからの取り直しを減らす初回と同じ割合以上で成功すると仮定するが、クエリの長さを考えて三倍まで
const countToGet = remainingToRead * Math.ceil(Math.min(1.1 / lastSuccessfulRate, 3));
noteIds = redisResultIds.slice(readFromRedis, readFromRedis + countToGet);
readFromRedis += noteIds.length;
const gotFromDb = await this.getAndFilterFromDb(noteIds, filter);
redisTimeline.push(...gotFromDb);
lastSuccessfulRate = gotFromDb.length / noteIds.length;
if (ps.allowPartial ? redisTimeline.length !== 0 : redisTimeline.length >= ps.limit) {
// 十分Redisからとれた
return redisTimeline.slice(0, ps.limit);
}
}
// まだ足りない分はDBにフォールバック
const remainingToRead = ps.limit - redisTimeline.length;
const gotFromDb = await ps.dbFallback(noteIds[noteIds.length - 1], ps.sinceId, remainingToRead);
redisTimeline.push(...gotFromDb);
return redisTimeline;
}
return await ps.dbFallback(ps.untilId, ps.sinceId, ps.limit);
}
private async getAndFilterFromDb(noteIds: string[], noteFilter: (note: MiNote) => boolean): Promise<MiNote[]> {
const query = this.notesRepository.createQueryBuilder('note')
.where('note.id IN (:...noteIds)', { noteIds: noteIds })
.innerJoinAndSelect('note.user', 'user')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
.leftJoinAndSelect('renote.user', 'renoteUser')
.leftJoinAndSelect('note.channel', 'channel');
const notes = (await query.getMany()).filter(noteFilter);
notes.sort((a, b) => a.id > b.id ? -1 : 1);
return notes;
}
}

View file

@ -9,6 +9,38 @@ import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
export type FanoutTimelineName =
// home timeline
| `homeTimeline:${string}`
| `homeTimelineWithFiles:${string}` // only notes with files are included
| `homeTimelineWithCats:${string}` // only notes with cats are included
// local timeline
| `localTimeline` // replies are not included
| `localTimelineWithFiles` // only non-reply notes with files are included
| `localTimelineWithReplies` // only replies are included
| `localTimelineWithCats` // only notes with cats are included
// antenna
| `antennaTimeline:${string}`
// user timeline
| `userTimeline:${string}` // replies are not included
| `userTimelineWithFiles:${string}` // only non-reply notes with files are included
| `userTimelineWithReplies:${string}` // only replies are included
| `userTimelineWithChannel:${string}` // only channel notes are included, replies are included
| `userTimelineWithCats:${string}` // only notes with cats are included
// user list timelines
| `userListTimeline:${string}`
| `userListTimelineWithFiles:${string}` // only notes with files are included
| `userListTimelineWithCats:${string}` // only notes with cats are included
// channel timelines
| `channelTimeline:${string}` // replies are included
// role timelines
| `roleTimeline:${string}` // any notes are included
@Injectable()
export class FanoutTimelineService {
constructor(
@ -20,7 +52,7 @@ export class FanoutTimelineService {
}
@bindThis
public push(tl: string, id: string, maxlen: number, pipeline: Redis.ChainableCommander) {
public push(tl: FanoutTimelineName, id: string, maxlen: number, pipeline: Redis.ChainableCommander) {
// リモートから遅れて届いた(もしくは後から追加された)投稿日時が古い投稿が追加されるとページネーション時に問題を引き起こすため、
// 3分以内に投稿されたものでない場合、Redisにある最古のIDより新しい場合のみ追加する
if (this.idService.parse(id).date.getTime() > Date.now() - 1000 * 60 * 3) {
@ -41,7 +73,7 @@ export class FanoutTimelineService {
}
@bindThis
public get(name: string, untilId?: string | null, sinceId?: string | null) {
public get(name: FanoutTimelineName, untilId?: string | null, sinceId?: string | null) {
if (untilId && sinceId) {
return this.redisForTimelines.lrange('list:' + name, 0, -1)
.then(ids => ids.filter(id => id < untilId && id > sinceId).sort((a, b) => a > b ? -1 : 1));
@ -58,7 +90,7 @@ export class FanoutTimelineService {
}
@bindThis
public getMulti(name: string[], untilId?: string | null, sinceId?: string | null): Promise<string[][]> {
public getMulti(name: FanoutTimelineName[], untilId?: string | null, sinceId?: string | null): Promise<string[][]> {
const pipeline = this.redisForTimelines.pipeline();
for (const n of name) {
pipeline.lrange('list:' + n, 0, -1);
@ -79,7 +111,7 @@ export class FanoutTimelineService {
}
@bindThis
public purge(name: string) {
public purge(name: FanoutTimelineName) {
return this.redisForTimelines.del('list:' + name);
}
}

View file

@ -7,11 +7,11 @@ import { Inject, Injectable } from '@nestjs/common';
import { ulid } from 'ulid';
import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js';
import { genAid, parseAid } from '@/misc/id/aid.js';
import { genAidx, parseAidx } from '@/misc/id/aidx.js';
import { genMeid, parseMeid } from '@/misc/id/meid.js';
import { genMeidg, parseMeidg } from '@/misc/id/meidg.js';
import { genObjectId, parseObjectId } from '@/misc/id/object-id.js';
import { genAid, isSafeAidT, parseAid } from '@/misc/id/aid.js';
import { genAidx, isSafeAidxT, parseAidx } from '@/misc/id/aidx.js';
import { genMeid, isSafeMeidT, parseMeid } from '@/misc/id/meid.js';
import { genMeidg, isSafeMeidgT, parseMeidg } from '@/misc/id/meidg.js';
import { genObjectId, isSafeObjectIdT, parseObjectId } from '@/misc/id/object-id.js';
import { bindThis } from '@/decorators.js';
import { parseUlid } from '@/misc/id/ulid.js';
@ -26,6 +26,19 @@ export class IdService {
this.method = config.id.toLowerCase();
}
@bindThis
public isSafeT(t: number): boolean {
switch (this.method) {
case 'aid': return isSafeAidT(t);
case 'aidx': return isSafeAidxT(t);
case 'meid': return isSafeMeidT(t);
case 'meidg': return isSafeMeidgT(t);
case 'ulid': return t > 0;
case 'objectid': return isSafeObjectIdT(t);
default: throw new Error('unrecognized id generation method');
}
}
/**
* IDを生成します()
* @param time

View file

@ -58,6 +58,7 @@ import { FeaturedService } from '@/core/FeaturedService.js';
import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
import { UtilityService } from '@/core/UtilityService.js';
import { UserBlockingService } from '@/core/UserBlockingService.js';
import { isReply } from '@/misc/is-reply.js';
type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';
@ -915,7 +916,7 @@ export class NoteCreateService implements OnApplicationShutdown {
if (note.visibility === 'specified' && !note.visibleUserIds.some(v => v === following.followerId)) continue;
// 「自分自身への返信 or そのフォロワーへの返信」のどちらでもない場合
if (note.replyId && !(note.replyUserId === note.userId || note.replyUserId === following.followerId)) {
if (isReply(note, following.followerId)) {
if (!following.withReplies) continue;
}
@ -933,7 +934,7 @@ export class NoteCreateService implements OnApplicationShutdown {
) continue;
// 「自分自身への返信 or そのリストの作成者への返信」のどちらでもない場合
if (note.replyId && !(note.replyUserId === note.userId || note.replyUserId === userListMembership.userListUserId)) {
if (isReply(note, userListMembership.userListUserId)) {
if (!userListMembership.withReplies) continue;
}
@ -951,7 +952,7 @@ export class NoteCreateService implements OnApplicationShutdown {
}
// 自分自身以外への返信
if (note.replyId && note.replyUserId !== note.userId) {
if (isReply(note)) {
this.fanoutTimelineService.push(`userTimelineWithReplies:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r);
if (note.visibility === 'public' && note.userHost == null) {

View file

@ -77,7 +77,7 @@ export class NotePiningService {
} as MiUserNotePining);
// Deliver to remote followers
if (this.userEntityService.isLocalUser(user)) {
if (this.userEntityService.isLocalUser(user) && !note.localOnly && ['public', 'home'].includes(note.visibility)) {
this.deliverPinnedChange(user.id, note.id, true);
}
}
@ -105,7 +105,7 @@ export class NotePiningService {
});
// Deliver to remote followers
if (this.userEntityService.isLocalUser(user)) {
if (this.userEntityService.isLocalUser(user) && !note.localOnly && ['public', 'home'].includes(note.visibility)) {
this.deliverPinnedChange(user.id, noteId, false);
}
}

View file

@ -304,8 +304,6 @@ export class UserFollowingService implements OnModuleInit {
});
}
});
this.fanoutTimelineService.purge(`homeTimeline:${follower.id}`);
}
// Publish followed event
@ -373,8 +371,6 @@ export class UserFollowingService implements OnModuleInit {
});
}
});
this.fanoutTimelineService.purge(`homeTimeline:${follower.id}`);
}
if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {

View file

@ -349,9 +349,15 @@ export class ApInboxService {
this.logger.info(`Creating the (Re)Note: ${uri}`);
const activityAudience = await this.apAudienceService.parseAudience(actor, activity.to, activity.cc);
const createdAt = activity.published ? new Date(activity.published) : null;
if (createdAt && createdAt < this.idService.parse(renote.id).date) {
this.logger.warn('skip: malformed createdAt');
return;
}
await this.noteCreateService.create(actor, {
createdAt: activity.published ? new Date(activity.published) : null,
createdAt,
renote,
visibility: activityAudience.visibility,
visibleUsers: activityAudience.visibleUsers,

View file

@ -104,6 +104,10 @@ export class ApNoteService {
return new Error(`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${actualHost}`);
}
if (object.published && !this.idService.isSafeT(new Date(object.published).valueOf())) {
return new Error('invalid Note: published timestamp is malformed');
}
return null;
}

View file

@ -34,3 +34,7 @@ export function parseAid(id: string): { date: Date; } {
const time = parseInt(id.slice(0, 8), 36) + TIME2000;
return { date: new Date(time) };
}
export function isSafeAidT(t: number): boolean {
return t > TIME2000;
}

View file

@ -41,3 +41,7 @@ export function parseAidx(id: string): { date: Date; } {
const time = parseInt(id.slice(0, TIME_LENGTH), 36) + TIME2000;
return { date: new Date(time) };
}
export function isSafeAidxT(t: number): boolean {
return t > TIME2000;
}

View file

@ -38,3 +38,7 @@ export function parseMeid(id: string): { date: Date; } {
date: new Date(parseInt(id.slice(0, 12), 16) - 0x800000000000),
};
}
export function isSafeMeidT(t: number): boolean {
return t > 0;
}

View file

@ -38,3 +38,7 @@ export function parseMeidg(id: string): { date: Date; } {
date: new Date(parseInt(id.slice(1, 12), 16)),
};
}
export function isSafeMeidgT(t: number): boolean {
return t > 0;
}

View file

@ -38,3 +38,7 @@ export function parseObjectId(id: string): { date: Date; } {
date: new Date(parseInt(id.slice(0, 8), 16) * 1000),
};
}
export function isSafeObjectIdT(t: number): boolean {
return t > 0;
}

View file

@ -3,12 +3,13 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { MiNote } from '@/models/Note.js';
import type { Packed } from './json-schema.js';
export function isInstanceMuted(note: Packed<'Note'>, mutedInstances: Set<string>): boolean {
if (mutedInstances.has(note.user.host ?? '')) return true;
if (mutedInstances.has(note.reply?.user.host ?? '')) return true;
if (mutedInstances.has(note.renote?.user.host ?? '')) return true;
export function isInstanceMuted(note: Packed<'Note'> | MiNote, mutedInstances: Set<string>): boolean {
if (mutedInstances.has(note.user?.host ?? '')) return true;
if (mutedInstances.has(note.reply?.user?.host ?? '')) return true;
if (mutedInstances.has(note.renote?.user?.host ?? '')) return true;
return false;
}

View file

@ -0,0 +1,10 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey, cherrypick contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { MiUser } from '@/models/User.js';
export function isReply(note: any, viewerId?: MiUser['id'] | undefined | null): boolean {
return note.replyId && note.replyUserId !== note.userId && note.replyUserId !== viewerId;
}

View file

@ -38,6 +38,7 @@ import { packedGalleryPostSchema } from '@/models/json-schema/gallery-post.js';
import { packedEmojiDetailedSchema, packedEmojiSimpleSchema } from '@/models/json-schema/emoji.js';
import { packedFlashSchema } from '@/models/json-schema/flash.js';
import { packedAnnouncementSchema } from '@/models/json-schema/announcement.js';
import { packedSigninSchema } from '@/models/json-schema/signin.js';
export const refs = {
UserLite: packedUserLiteSchema,
@ -75,6 +76,7 @@ export const refs = {
EmojiSimple: packedEmojiSimpleSchema,
EmojiDetailed: packedEmojiDetailedSchema,
Flash: packedFlashSchema,
Signin: packedSigninSchema,
};
export type Packed<x extends keyof typeof refs> = SchemaType<typeof refs[x]>;

View file

@ -0,0 +1,26 @@
export const packedSigninSchema = {
type: 'object',
properties: {
id: {
type: 'string',
optional: false, nullable: false,
},
createdAt: {
type: 'string',
optional: false, nullable: false,
format: 'date-time',
},
ip: {
type: 'string',
optional: false, nullable: false,
},
headers: {
type: 'object',
optional: false, nullable: false,
},
success: {
type: 'boolean',
optional: false, nullable: false,
},
},
} as const;

View file

@ -370,8 +370,9 @@ export class ActivityPubServerService {
order: { id: 'DESC' },
});
const pinnedNotes = await Promise.all(pinings.map(pining =>
this.notesRepository.findOneByOrFail({ id: pining.noteId })));
const pinnedNotes = (await Promise.all(pinings.map(pining =>
this.notesRepository.findOneByOrFail({ id: pining.noteId }))))
.filter(note => !note.localOnly && ['public', 'home'].includes(note.visibility));
const renderedNotes = await Promise.all(pinnedNotes.map(note => this.apRendererService.renderNote(note)));

View file

@ -101,6 +101,11 @@ export class NodeinfoServerService {
metadata: {
nodeName: meta.name,
nodeDescription: meta.description,
nodeAdmins: [{
name: meta.maintainerName,
email: meta.maintainerEmail,
}],
// deprecated
maintainer: {
name: meta.maintainerName,
email: meta.maintainerEmail,

View file

@ -119,8 +119,8 @@ export class ServerService implements OnApplicationShutdown {
return;
}
const name = path.split('@')[0].replace('.webp', '');
const host = path.split('@')[1]?.replace('.webp', '');
const name = path.split('@')[0].replace(/\.webp$/i, '');
const host = path.split('@')[1]?.replace(/\.webp$/i, '');
const emoji = await this.emojisRepository.findOneBy({
// `@.` is the spec of ReactionService.decodeReaction

View file

@ -6,11 +6,10 @@
import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import type { EmojisRepository } from '@/models/_.js';
import { IdService } from '@/core/IdService.js';
import type { MiDriveFile } from '@/models/DriveFile.js';
import { DI } from '@/di-symbols.js';
import { DriveService } from '@/core/DriveService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
import { ApiError } from '../../../error.js';
@ -26,6 +25,11 @@ export const meta = {
code: 'NO_SUCH_EMOJI',
id: 'e2785b66-dca3-4087-9cac-b93c541cc425',
},
duplicateName: {
message: 'Duplicate name.',
code: 'DUPLICATE_NAME',
id: 'f7a3462c-4e6e-4069-8421-b9bd4f4c3975',
},
},
res: {
@ -56,15 +60,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
constructor(
@Inject(DI.emojisRepository)
private emojisRepository: EmojisRepository,
private emojiEntityService: EmojiEntityService,
private idService: IdService,
private globalEventService: GlobalEventService,
private customEmojiService: CustomEmojiService,
private driveService: DriveService,
) {
super(meta, paramDef, async (ps, me) => {
const emoji = await this.emojisRepository.findOneBy({ id: ps.emojiId });
if (emoji == null) {
throw new ApiError(meta.errors.noSuchEmoji);
}
@ -75,28 +76,27 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
// Create file
driveFile = await this.driveService.uploadFromUrl({ url: emoji.originalUrl, user: null, force: true });
} catch (e) {
// TODO: need to return Drive Error
throw new ApiError();
}
const copied = await this.emojisRepository.insert({
id: this.idService.gen(),
updatedAt: new Date(),
// Duplication Check
const isDuplicate = await this.customEmojiService.checkDuplicate(emoji.name);
if (isDuplicate) throw new ApiError(meta.errors.duplicateName);
const addedEmoji = await this.customEmojiService.add({
driveFile,
name: emoji.name,
category: emoji.category,
aliases: emoji.aliases,
host: null,
aliases: [],
originalUrl: driveFile.url,
publicUrl: driveFile.webpublicUrl ?? driveFile.url,
type: driveFile.webpublicType ?? driveFile.type,
license: emoji.license,
}).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0]));
isSensitive: emoji.isSensitive,
localOnly: emoji.localOnly,
roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction,
}, me);
this.globalEventService.publishBroadcastStream('emojiAdded', {
emoji: await this.emojiEntityService.packDetailed(copied.id),
});
return {
id: copied.id,
};
return this.emojiEntityService.packDetailed(addedEmoji);
});
}
}

View file

@ -7,11 +7,10 @@ import { Inject, Injectable } from '@nestjs/common';
import { IsNull } from 'typeorm';
import { Endpoint } from '@/server/api/endpoint-base.js';
import type { EmojisRepository } from '@/models/_.js';
import { IdService } from '@/core/IdService.js';
import type { MiDriveFile } from '@/models/DriveFile.js';
import { DI } from '@/di-symbols.js';
import { DriveService } from '@/core/DriveService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
import { ApiError } from '../../../error.js';
@ -27,6 +26,11 @@ export const meta = {
code: 'NO_SUCH_EMOJI',
id: 'e2785b66-dca3-4087-9cac-b93c541cc425',
},
duplicateName: {
message: 'Duplicate name.',
code: 'DUPLICATE_NAME',
id: 'f7a3462c-4e6e-4069-8421-b9bd4f4c3975',
},
localEmojiAlreadyExists: {
message: 'Local emoji already exists.',
code: 'LOCAL_EMOJI_ALREADY_EXISTS',
@ -63,10 +67,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
constructor(
@Inject(DI.emojisRepository)
private emojisRepository: EmojisRepository,
private emojiEntityService: EmojiEntityService,
private idService: IdService,
private globalEventService: GlobalEventService,
private customEmojiService: CustomEmojiService,
private driveService: DriveService,
) {
super(meta, paramDef, async (ps, me) => {
@ -87,28 +89,27 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
// Create file
driveFile = await this.driveService.uploadFromUrl({ url: emoji.originalUrl, user: null, force: true });
} catch (e) {
// TODO: need to return Drive Error
throw new ApiError();
}
const copied = await this.emojisRepository.insert({
id: this.idService.gen(),
updatedAt: new Date(),
// Duplication Check
const isDuplicate = await this.customEmojiService.checkDuplicate(emoji.name);
if (isDuplicate) throw new ApiError(meta.errors.duplicateName);
const addedEmoji = await this.customEmojiService.add({
driveFile,
name: emoji.name,
category: emoji.category,
aliases: emoji.aliases,
host: null,
aliases: [],
originalUrl: driveFile.url,
publicUrl: driveFile.webpublicUrl ?? driveFile.url,
type: driveFile.webpublicType ?? driveFile.type,
license: emoji.license,
}).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0]));
isSensitive: emoji.isSensitive,
localOnly: emoji.localOnly,
roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction,
}, me);
this.globalEventService.publishBroadcastStream('emojiAdded', {
emoji: await this.emojiEntityService.packDetailed(copied.id),
});
return {
id: copied.id,
};
return this.emojiEntityService.packDetailed(addedEmoji);
});
}
}

View file

@ -387,6 +387,82 @@ export const meta = {
type: 'number',
optional: false, nullable: false,
},
backgroundImageUrl: {
type: 'string',
optional: false, nullable: true,
},
deeplAuthKey: {
type: 'string',
optional: false, nullable: true,
},
deeplIsPro: {
type: 'boolean',
optional: false, nullable: false,
},
defaultDarkTheme: {
type: 'string',
optional: false, nullable: true,
},
defaultLightTheme: {
type: 'string',
optional: false, nullable: true,
},
description: {
type: 'string',
optional: false, nullable: true,
},
disableRegistration: {
type: 'boolean',
optional: false, nullable: false,
},
impressumUrl: {
type: 'string',
optional: false, nullable: true,
},
maintainerEmail: {
type: 'string',
optional: false, nullable: true,
},
maintainerName: {
type: 'string',
optional: false, nullable: true,
},
name: {
type: 'string',
optional: false, nullable: true,
},
objectStorageS3ForcePathStyle: {
type: 'boolean',
optional: false, nullable: false,
},
privacyPolicyUrl: {
type: 'string',
optional: false, nullable: true,
},
repositoryUrl: {
type: 'string',
optional: false, nullable: false,
},
summalyProxy: {
type: 'string',
optional: false, nullable: true,
},
themeColor: {
type: 'string',
optional: false, nullable: true,
},
tosUrl: {
type: 'string',
optional: false, nullable: true,
},
uri: {
type: 'string',
optional: false, nullable: false,
},
version: {
type: 'string',
optional: false, nullable: false,
},
enableReceivePrerelease: {
type: 'boolean',
optional: false, nullable: false,

View file

@ -4,18 +4,17 @@
*/
import { Inject, Injectable } from '@nestjs/common';
import * as Redis from 'ioredis';
import { Endpoint } from '@/server/api/endpoint-base.js';
import type { ChannelsRepository, MiNote, NotesRepository } from '@/models/_.js';
import type { ChannelsRepository, NotesRepository } from '@/models/_.js';
import { QueryService } from '@/core/QueryService.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import ActiveUsersChart from '@/core/chart/charts/active-users.js';
import { DI } from '@/di-symbols.js';
import { IdService } from '@/core/IdService.js';
import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { CacheService } from '@/core/CacheService.js';
import { MetaService } from '@/core/MetaService.js';
import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
import { MiLocalUser } from '@/models/User.js';
import { ApiError } from '../../error.js';
export const meta = {
@ -51,6 +50,7 @@ export const paramDef = {
untilId: { type: 'string', format: 'misskey:id' },
sinceDate: { type: 'integer' },
untilDate: { type: 'integer' },
allowPartial: { type: 'boolean', default: false }, // true is recommended but for compatibility false by default
},
required: ['channelId'],
} as const;
@ -58,9 +58,6 @@ export const paramDef = {
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
@Inject(DI.redisForTimelines)
private redisForTimelines: Redis.Redis,
@Inject(DI.notesRepository)
private notesRepository: NotesRepository,
@ -70,7 +67,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private idService: IdService,
private noteEntityService: NoteEntityService,
private queryService: QueryService,
private fanoutTimelineService: FanoutTimelineService,
private fanoutTimelineEndpointService: FanoutTimelineEndpointService,
private cacheService: CacheService,
private activeUsersChart: ActiveUsersChart,
private metaService: MetaService,
@ -78,7 +75,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
super(meta, paramDef, async (ps, me) => {
const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null);
const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : null);
const isRangeSpecified = untilId != null && sinceId != null;
const serverSettings = await this.metaService.fetch();
@ -92,64 +88,48 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (me) this.activeUsersChart.read(me);
if (serverSettings.enableFanoutTimeline && (isRangeSpecified || sinceId == null)) {
const [
userIdsWhoMeMuting,
] = me ? await Promise.all([
this.cacheService.userMutingsCache.fetch(me.id),
]) : [new Set<string>()];
let noteIds = await this.fanoutTimelineService.get(`channelTimeline:${channel.id}`, untilId, sinceId);
noteIds = noteIds.slice(0, ps.limit);
if (noteIds.length > 0) {
const query = this.notesRepository.createQueryBuilder('note')
.where('note.id IN (:...noteIds)', { noteIds: noteIds })
.innerJoinAndSelect('note.user', 'user')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
.leftJoinAndSelect('renote.user', 'renoteUser')
.leftJoinAndSelect('note.channel', 'channel');
let timeline = await query.getMany();
timeline = timeline.filter(note => {
if (me && isUserRelated(note, userIdsWhoMeMuting)) return false;
return true;
});
// TODO: フィルタで件数が減った場合の埋め合わせ処理
timeline.sort((a, b) => a.id > b.id ? -1 : 1);
if (timeline.length > 0) {
return await this.noteEntityService.packMany(timeline, me);
}
}
if (!serverSettings.enableFanoutTimeline) {
return await this.noteEntityService.packMany(await this.getFromDb({ untilId, sinceId, limit: ps.limit, channelId: channel.id }, me), me);
}
//#region fallback to database
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
.andWhere('note.channelId = :channelId', { channelId: channel.id })
.innerJoinAndSelect('note.user', 'user')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
.leftJoinAndSelect('renote.user', 'renoteUser')
.leftJoinAndSelect('note.channel', 'channel');
if (me) {
this.queryService.generateMutedUserQuery(query, me);
this.queryService.generateBlockedUserQuery(query, me);
}
//#endregion
const timeline = await query.limit(ps.limit).getMany();
return await this.noteEntityService.packMany(timeline, me);
//#endregion
return await this.fanoutTimelineEndpointService.timeline({
untilId,
sinceId,
limit: ps.limit,
allowPartial: ps.allowPartial,
me,
useDbFallback: true,
redisTimelines: [`channelTimeline:${channel.id}`],
excludePureRenotes: false,
dbFallback: async (untilId, sinceId, limit) => {
return await this.getFromDb({ untilId, sinceId, limit, channelId: channel.id }, me);
},
});
});
}
private async getFromDb(ps: {
untilId: string | null,
sinceId: string | null,
limit: number,
channelId: string
}, me: MiLocalUser | null) {
//#region fallback to database
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId)
.andWhere('note.channelId = :channelId', { channelId: ps.channelId })
.innerJoinAndSelect('note.user', 'user')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
.leftJoinAndSelect('renote.user', 'renoteUser')
.leftJoinAndSelect('note.channel', 'channel');
if (me) {
this.queryService.generateMutedUserQuery(query, me);
this.queryService.generateBlockedUserQuery(query, me);
}
//#endregion
return await query.limit(ps.limit).getMany();
}
}

View file

@ -42,7 +42,26 @@ export const paramDef = {
publishing: { type: 'boolean', nullable: true },
limit: { type: 'integer', minimum: 1, maximum: 100, default: 30 },
offset: { type: 'integer', default: 0 },
sort: { type: 'string' },
sort: {
type: 'string',
nullable: true,
enum: [
'+pubSub',
'-pubSub',
'+notes',
'-notes',
'+users',
'-users',
'+following',
'-following',
'+followers',
'-followers',
'+firstRetrievedAt',
'-firstRetrievedAt',
'+latestRequestReceivedAt',
'-latestRequestReceivedAt',
],
},
},
required: [],
} as const;

View file

@ -12,8 +12,17 @@ import { DI } from '@/di-symbols.js';
export const meta = {
requireCredential: true,
secure: true,
res: {
type: 'array',
optional: false, nullable: false,
items: {
type: 'object',
optional: false, nullable: false,
ref: 'Signin',
},
},
} as const;
export const paramDef = {

View file

@ -254,6 +254,33 @@ export const meta = {
},
},
},
backgroundImageUrl: {
type: 'string',
optional: false, nullable: true,
},
impressumUrl: {
type: 'string',
optional: false, nullable: true,
},
logoImageUrl: {
type: 'string',
optional: false, nullable: true,
},
privacyPolicyUrl: {
type: 'string',
optional: false, nullable: true,
},
serverRules: {
type: 'array',
optional: false, nullable: false,
items: {
type: 'string',
},
},
themeColor: {
type: 'string',
optional: false, nullable: true,
},
},
},
} as const;

View file

@ -9,6 +9,8 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { DI } from '@/di-symbols.js';
import { FeaturedService } from '@/core/FeaturedService.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { CacheService } from '@/core/CacheService.js';
export const meta = {
tags: ['notes'],
@ -47,6 +49,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
@Inject(DI.notesRepository)
private notesRepository: NotesRepository,
private cacheService: CacheService,
private noteEntityService: NoteEntityService,
private featuredService: FeaturedService,
) {
@ -74,6 +77,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
return [];
}
const [
userIdsWhoMeMuting,
userIdsWhoBlockingMe,
] = me ? await Promise.all([
this.cacheService.userMutingsCache.fetch(me.id),
this.cacheService.userBlockedCache.fetch(me.id),
]) : [new Set<string>(), new Set<string>()];
const query = this.notesRepository.createQueryBuilder('note')
.where('note.id IN (:...noteIds)', { noteIds: noteIds })
.innerJoinAndSelect('note.user', 'user')
@ -83,10 +94,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.leftJoinAndSelect('renote.user', 'renoteUser')
.leftJoinAndSelect('note.channel', 'channel');
const notes = await query.getMany();
notes.sort((a, b) => a.id > b.id ? -1 : 1);
const notes = (await query.getMany()).filter(note => {
if (me && isUserRelated(note, userIdsWhoBlockingMe)) return false;
if (me && isUserRelated(note, userIdsWhoMeMuting)) return false;
// TODO: ミュート等考慮
return true;
});
notes.sort((a, b) => a.id > b.id ? -1 : 1);
return await this.noteEntityService.packMany(notes, me);
});

View file

@ -5,20 +5,20 @@
import { Brackets } from 'typeorm';
import { Inject, Injectable } from '@nestjs/common';
import type { NotesRepository, FollowingsRepository, MiNote, ChannelFollowingsRepository } from '@/models/_.js';
import type { NotesRepository, ChannelFollowingsRepository } from '@/models/_.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import ActiveUsersChart from '@/core/chart/charts/active-users.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { DI } from '@/di-symbols.js';
import { RoleService } from '@/core/RoleService.js';
import { IdService } from '@/core/IdService.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { CacheService } from '@/core/CacheService.js';
import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
import { FanoutTimelineName } from '@/core/FanoutTimelineService.js';
import { QueryService } from '@/core/QueryService.js';
import { UserFollowingService } from '@/core/UserFollowingService.js';
import { MetaService } from '@/core/MetaService.js';
import { MiLocalUser } from '@/models/User.js';
import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
import { ApiError } from '../../error.js';
export const meta = {
@ -42,6 +42,12 @@ export const meta = {
code: 'STL_DISABLED',
id: '620763f4-f621-4533-ab33-0577a1a3c342',
},
bothWithRepliesAndWithFiles: {
message: 'Specifying both withReplies and withFiles is not supported',
code: 'BOTH_WITH_REPLIES_AND_WITH_FILES',
id: 'dfaa3eb7-8002-4cb7-bcc4-1095df46656f'
},
},
} as const;
@ -53,6 +59,7 @@ export const paramDef = {
untilId: { type: 'string', format: 'misskey:id' },
sinceDate: { type: 'integer' },
untilDate: { type: 'integer' },
allowPartial: { type: 'boolean', default: false }, // true is recommended but for compatibility false by default
includeMyRenotes: { type: 'boolean', default: true },
includeRenotedMyNotes: { type: 'boolean', default: true },
includeLocalRenotes: { type: 'boolean', default: true },
@ -78,10 +85,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private activeUsersChart: ActiveUsersChart,
private idService: IdService,
private cacheService: CacheService,
private fanoutTimelineService: FanoutTimelineService,
private queryService: QueryService,
private userFollowingService: UserFollowingService,
private metaService: MetaService,
private fanoutTimelineEndpointService: FanoutTimelineEndpointService,
) {
super(meta, paramDef, async (ps, me) => {
const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null);
@ -92,10 +99,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
throw new ApiError(meta.errors.stlDisabled);
}
if (ps.withReplies && ps.withFiles) throw new ApiError(meta.errors.bothWithRepliesAndWithFiles);
const serverSettings = await this.metaService.fetch();
if (!serverSettings.enableFanoutTimeline) {
return await this.getFromDb({
const timeline = await this.getFromDb({
untilId,
sinceId,
limit: ps.limit,
@ -106,108 +115,62 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
withReplies: ps.withReplies,
withCats: ps.withCats,
}, me);
}
const [
userIdsWhoMeMuting,
userIdsWhoMeMutingRenotes,
userIdsWhoBlockingMe,
] = await Promise.all([
this.cacheService.userMutingsCache.fetch(me.id),
this.cacheService.renoteMutingsCache.fetch(me.id),
this.cacheService.userBlockedCache.fetch(me.id),
]);
let noteIds: string[];
let shouldFallbackToDb = false;
if (ps.withFiles) {
const [htlNoteIds, ltlNoteIds] = await this.fanoutTimelineService.getMulti([
`homeTimelineWithFiles:${me.id}`,
'localTimelineWithFiles',
], untilId, sinceId);
noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds]));
} else if (ps.withReplies) {
const [htlNoteIds, ltlNoteIds, ltlReplyNoteIds] = await this.fanoutTimelineService.getMulti([
`homeTimeline:${me.id}`,
'localTimeline',
'localTimelineWithReplies',
], untilId, sinceId);
noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds, ...ltlReplyNoteIds]));
} else {
const [htlNoteIds, ltlNoteIds] = await this.fanoutTimelineService.getMulti([
`homeTimeline:${me.id}`,
'localTimeline',
], untilId, sinceId);
noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds]));
shouldFallbackToDb = htlNoteIds.length === 0;
}
noteIds.sort((a, b) => a > b ? -1 : 1);
noteIds = noteIds.slice(0, ps.limit);
shouldFallbackToDb = shouldFallbackToDb || (noteIds.length === 0);
let redisTimeline: MiNote[] = [];
if (!shouldFallbackToDb) {
const query = this.notesRepository.createQueryBuilder('note')
.where('note.id IN (:...noteIds)', { noteIds: noteIds })
.innerJoinAndSelect('note.user', 'user')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
.leftJoinAndSelect('renote.user', 'renoteUser')
.leftJoinAndSelect('note.channel', 'channel');
if (ps.withCats) {
query.andWhere('(select "isCat" from "user" where id = note."userId")');
}
redisTimeline = await query.getMany();
redisTimeline = redisTimeline.filter(note => {
if (note.userId === me.id) {
return true;
}
if (isUserRelated(note, userIdsWhoBlockingMe)) return false;
if (isUserRelated(note, userIdsWhoMeMuting)) return false;
if (note.renoteId) {
if (note.text == null && note.fileIds.length === 0 && !note.hasPoll) {
if (isUserRelated(note, userIdsWhoMeMutingRenotes)) return false;
if (ps.withRenotes === false) return false;
}
}
return true;
});
redisTimeline.sort((a, b) => a.id > b.id ? -1 : 1);
}
if (redisTimeline.length > 0) {
process.nextTick(() => {
this.activeUsersChart.read(me);
});
return await this.noteEntityService.packMany(redisTimeline, me);
} else {
if (serverSettings.enableFanoutTimelineDbFallback) { // fallback to db
return await this.getFromDb({
untilId,
sinceId,
limit: ps.limit,
includeMyRenotes: ps.includeMyRenotes,
includeRenotedMyNotes: ps.includeRenotedMyNotes,
includeLocalRenotes: ps.includeLocalRenotes,
withFiles: ps.withFiles,
withReplies: ps.withReplies,
withCats: ps.withCats,
}, me);
} else {
return [];
}
return await this.noteEntityService.packMany(timeline, me);
}
let timelineConfig: FanoutTimelineName[];
if (ps.withFiles) {
timelineConfig = [
`homeTimelineWithFiles:${me.id}`,
'localTimelineWithFiles',
];
} else if (ps.withReplies) {
timelineConfig = [
`homeTimeline:${me.id}`,
'localTimeline',
'localTimelineWithReplies',
];
} else {
timelineConfig = [
`homeTimeline:${me.id}`,
'localTimeline',
];
}
const redisTimeline = await this.fanoutTimelineEndpointService.timeline({
untilId,
sinceId,
limit: ps.limit,
allowPartial: ps.allowPartial,
me,
redisTimelines: timelineConfig,
useDbFallback: serverSettings.enableFanoutTimelineDbFallback,
alwaysIncludeMyNotes: true,
excludePureRenotes: !ps.withRenotes,
dbFallback: async (untilId, sinceId, limit) => await this.getFromDb({
untilId,
sinceId,
limit,
includeMyRenotes: ps.includeMyRenotes,
includeRenotedMyNotes: ps.includeRenotedMyNotes,
includeLocalRenotes: ps.includeLocalRenotes,
withFiles: ps.withFiles,
withReplies: ps.withReplies,
withCats: ps.withCats,
}, me),
});
process.nextTick(() => {
this.activeUsersChart.read(me);
});
return redisTimeline;
});
}
@ -313,12 +276,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
//#endregion
const timeline = await query.limit(ps.limit).getMany();
process.nextTick(() => {
this.activeUsersChart.read(me);
});
return await this.noteEntityService.packMany(timeline, me);
return await query.limit(ps.limit).getMany();
}
}

View file

@ -5,7 +5,7 @@
import { Brackets } from 'typeorm';
import { Inject, Injectable } from '@nestjs/common';
import type { MiNote, NotesRepository } from '@/models/_.js';
import type { NotesRepository } from '@/models/_.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import ActiveUsersChart from '@/core/chart/charts/active-users.js';
@ -13,11 +13,10 @@ import { DI } from '@/di-symbols.js';
import { RoleService } from '@/core/RoleService.js';
import { IdService } from '@/core/IdService.js';
import { CacheService } from '@/core/CacheService.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
import { QueryService } from '@/core/QueryService.js';
import { MetaService } from '@/core/MetaService.js';
import { MiLocalUser } from '@/models/User.js';
import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
import { ApiError } from '../../error.js';
export const meta = {
@ -39,6 +38,12 @@ export const meta = {
code: 'LTL_DISABLED',
id: '45a6eb02-7695-4393-b023-dd3be9aaaefd',
},
bothWithRepliesAndWithFiles: {
message: 'Specifying both withReplies and withFiles is not supported',
code: 'BOTH_WITH_REPLIES_AND_WITH_FILES',
id: 'dd9c8400-1cb5-4eef-8a31-200c5f933793'
},
},
} as const;
@ -49,10 +54,10 @@ export const paramDef = {
withRenotes: { type: 'boolean', default: true },
withReplies: { type: 'boolean', default: false },
withCats: { type: 'boolean', default: false },
excludeNsfw: { type: 'boolean', default: false },
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
sinceId: { type: 'string', format: 'misskey:id' },
untilId: { type: 'string', format: 'misskey:id' },
allowPartial: { type: 'boolean', default: false }, // true is recommended but for compatibility false by default
sinceDate: { type: 'integer' },
untilDate: { type: 'integer' },
},
@ -70,7 +75,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private activeUsersChart: ActiveUsersChart,
private idService: IdService,
private cacheService: CacheService,
private fanoutTimelineService: FanoutTimelineService,
private fanoutTimelineEndpointService: FanoutTimelineEndpointService,
private queryService: QueryService,
private metaService: MetaService,
) {
@ -83,10 +88,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
throw new ApiError(meta.errors.ltlDisabled);
}
if (ps.withReplies && ps.withFiles) throw new ApiError(meta.errors.bothWithRepliesAndWithFiles);
const serverSettings = await this.metaService.fetch();
if (!serverSettings.enableFanoutTimeline) {
return await this.getFromDb({
const timeline = await this.getFromDb({
untilId,
sinceId,
limit: ps.limit,
@ -94,93 +101,44 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
withReplies: ps.withReplies,
withCats: ps.withCats,
}, me);
}
const [
userIdsWhoMeMuting,
userIdsWhoMeMutingRenotes,
userIdsWhoBlockingMe,
] = me ? await Promise.all([
this.cacheService.userMutingsCache.fetch(me.id),
this.cacheService.renoteMutingsCache.fetch(me.id),
this.cacheService.userBlockedCache.fetch(me.id),
]) : [new Set<string>(), new Set<string>(), new Set<string>()];
let noteIds: string[];
if (ps.withFiles) {
noteIds = await this.fanoutTimelineService.get('localTimelineWithFiles', untilId, sinceId);
} else {
const [nonReplyNoteIds, replyNoteIds] = await this.fanoutTimelineService.getMulti([
'localTimeline',
'localTimelineWithReplies',
], untilId, sinceId);
noteIds = Array.from(new Set([...nonReplyNoteIds, ...replyNoteIds]));
noteIds.sort((a, b) => a > b ? -1 : 1);
}
noteIds = noteIds.slice(0, ps.limit);
let redisTimeline: MiNote[] = [];
if (noteIds.length > 0) {
const query = this.notesRepository.createQueryBuilder('note')
.where('note.id IN (:...noteIds)', { noteIds: noteIds })
.innerJoinAndSelect('note.user', 'user')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
.leftJoinAndSelect('renote.user', 'renoteUser')
.leftJoinAndSelect('note.channel', 'channel');
if (ps.withCats) {
query.andWhere('(select "isCat" from "user" where id = note."userId")');
}
redisTimeline = await query.getMany();
redisTimeline = redisTimeline.filter(note => {
if (me && (note.userId === me.id)) {
return true;
}
if (!ps.withReplies && note.replyId && note.replyUserId !== note.userId && (me == null || note.replyUserId !== me.id)) return false;
if (me && isUserRelated(note, userIdsWhoBlockingMe)) return false;
if (me && isUserRelated(note, userIdsWhoMeMuting)) return false;
if (note.renoteId) {
if (note.text == null && note.fileIds.length === 0 && !note.hasPoll) {
if (me && isUserRelated(note, userIdsWhoMeMutingRenotes)) return false;
if (ps.withRenotes === false) return false;
}
}
return true;
});
redisTimeline.sort((a, b) => a.id > b.id ? -1 : 1);
}
if (redisTimeline.length > 0) {
process.nextTick(() => {
if (me) {
this.activeUsersChart.read(me);
}
});
return await this.noteEntityService.packMany(redisTimeline, me);
} else {
if (serverSettings.enableFanoutTimelineDbFallback) { // fallback to db
return await this.getFromDb({
untilId,
sinceId,
limit: ps.limit,
withFiles: ps.withFiles,
withReplies: ps.withReplies,
withCats: ps.withCats,
}, me);
} else {
return [];
}
return await this.noteEntityService.packMany(timeline, me);
}
const timeline = await this.fanoutTimelineEndpointService.timeline({
untilId,
sinceId,
limit: ps.limit,
allowPartial: ps.allowPartial,
me,
useDbFallback: serverSettings.enableFanoutTimelineDbFallback,
redisTimelines: ps.withFiles ? ['localTimelineWithFiles'] : ['localTimeline', 'localTimelineWithReplies'],
alwaysIncludeMyNotes: true,
excludeReplies: !ps.withReplies,
excludePureRenotes: !ps.withRenotes,
dbFallback: async (untilId, sinceId, limit) => await this.getFromDb({
untilId,
sinceId,
limit,
withFiles: ps.withFiles,
withReplies: ps.withReplies,
withCats: ps.withCats,
}, me),
});
process.nextTick(() => {
if (me) {
this.activeUsersChart.read(me);
}
});
return timeline;
});
}
@ -226,14 +184,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
query.andWhere('(select "isCat" from "user" where id = note."userId")');
}
const timeline = await query.limit(ps.limit).getMany();
process.nextTick(() => {
if (me) {
this.activeUsersChart.read(me);
}
});
return await this.noteEntityService.packMany(timeline, me);
return await query.limit(ps.limit).getMany();
}
}

View file

@ -5,7 +5,7 @@
import { Brackets } from 'typeorm';
import { Inject, Injectable } from '@nestjs/common';
import type { MiNote, NotesRepository, ChannelFollowingsRepository } from '@/models/_.js';
import type { NotesRepository, ChannelFollowingsRepository } from '@/models/_.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { QueryService } from '@/core/QueryService.js';
import ActiveUsersChart from '@/core/chart/charts/active-users.js';
@ -13,11 +13,10 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { DI } from '@/di-symbols.js';
import { IdService } from '@/core/IdService.js';
import { CacheService } from '@/core/CacheService.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
import { UserFollowingService } from '@/core/UserFollowingService.js';
import { MiLocalUser } from '@/models/User.js';
import { MetaService } from '@/core/MetaService.js';
import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
export const meta = {
tags: ['notes'],
@ -43,6 +42,7 @@ export const paramDef = {
untilId: { type: 'string', format: 'misskey:id' },
sinceDate: { type: 'integer' },
untilDate: { type: 'integer' },
allowPartial: { type: 'boolean', default: false }, // true is recommended but for compatibility false by default
includeMyRenotes: { type: 'boolean', default: true },
includeRenotedMyNotes: { type: 'boolean', default: true },
includeLocalRenotes: { type: 'boolean', default: true },
@ -66,7 +66,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private activeUsersChart: ActiveUsersChart,
private idService: IdService,
private cacheService: CacheService,
private fanoutTimelineService: FanoutTimelineService,
private fanoutTimelineEndpointService: FanoutTimelineEndpointService,
private userFollowingService: UserFollowingService,
private queryService: QueryService,
private metaService: MetaService,
@ -78,7 +78,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
const serverSettings = await this.metaService.fetch();
if (!serverSettings.enableFanoutTimeline) {
return await this.getFromDb({
const timeline = await this.getFromDb({
untilId,
sinceId,
limit: ps.limit,
@ -89,86 +89,55 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
withRenotes: ps.withRenotes,
withCats: ps.withCats,
}, me);
process.nextTick(() => {
this.activeUsersChart.read(me);
});
return await this.noteEntityService.packMany(timeline, me);
}
const [
followings,
userIdsWhoMeMuting,
userIdsWhoMeMutingRenotes,
userIdsWhoBlockingMe,
] = await Promise.all([
this.cacheService.userFollowingsCache.fetch(me.id),
this.cacheService.userMutingsCache.fetch(me.id),
this.cacheService.renoteMutingsCache.fetch(me.id),
this.cacheService.userBlockedCache.fetch(me.id),
]);
let noteIds = await this.fanoutTimelineService.get(ps.withFiles ? `homeTimelineWithFiles:${me.id}` : `homeTimeline:${me.id}`, untilId, sinceId);
noteIds = noteIds.slice(0, ps.limit);
let redisTimeline: MiNote[] = [];
if (noteIds.length > 0) {
const query = this.notesRepository.createQueryBuilder('note')
.where('note.id IN (:...noteIds)', { noteIds: noteIds })
.innerJoinAndSelect('note.user', 'user')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
.leftJoinAndSelect('renote.user', 'renoteUser')
.leftJoinAndSelect('note.channel', 'channel');
if (ps.withCats) {
query.andWhere('(select "isCat" from "user" where id = note."userId")');
}
redisTimeline = await query.getMany();
redisTimeline = redisTimeline.filter(note => {
if (note.userId === me.id) {
return true;
}
if (isUserRelated(note, userIdsWhoBlockingMe)) return false;
if (isUserRelated(note, userIdsWhoMeMuting)) return false;
if (note.renoteId) {
if (note.text == null && note.fileIds.length === 0 && !note.hasPoll) {
if (isUserRelated(note, userIdsWhoMeMutingRenotes)) return false;
if (ps.withRenotes === false) return false;
}
}
const timeline = this.fanoutTimelineEndpointService.timeline({
untilId,
sinceId,
limit: ps.limit,
allowPartial: ps.allowPartial,
me,
useDbFallback: serverSettings.enableFanoutTimelineDbFallback,
redisTimelines: ps.withFiles ? [`homeTimelineWithFiles:${me.id}`] : [`homeTimeline:${me.id}`],
alwaysIncludeMyNotes: true,
excludePureRenotes: !ps.withRenotes,
noteFilter: note => {
if (note.reply && note.reply.visibility === 'followers') {
if (!Object.hasOwn(followings, note.reply.userId)) return false;
}
return true;
});
},
dbFallback: async (untilId, sinceId, limit) => await this.getFromDb({
untilId,
sinceId,
limit,
includeMyRenotes: ps.includeMyRenotes,
includeRenotedMyNotes: ps.includeRenotedMyNotes,
includeLocalRenotes: ps.includeLocalRenotes,
withFiles: ps.withFiles,
withRenotes: ps.withRenotes,
withCats: ps.withCats,
}, me),
});
redisTimeline.sort((a, b) => a.id > b.id ? -1 : 1);
}
process.nextTick(() => {
this.activeUsersChart.read(me);
});
if (redisTimeline.length > 0) {
process.nextTick(() => {
this.activeUsersChart.read(me);
});
return await this.noteEntityService.packMany(redisTimeline, me);
} else {
if (serverSettings.enableFanoutTimelineDbFallback) { // fallback to db
return await this.getFromDb({
untilId,
sinceId,
limit: ps.limit,
includeMyRenotes: ps.includeMyRenotes,
includeRenotedMyNotes: ps.includeRenotedMyNotes,
includeLocalRenotes: ps.includeLocalRenotes,
withFiles: ps.withFiles,
withRenotes: ps.withRenotes,
withCats: ps.withCats,
}, me);
} else {
return [];
}
}
return timeline;
});
}
@ -280,12 +249,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
//#endregion
const timeline = await query.limit(ps.limit).getMany();
process.nextTick(() => {
this.activeUsersChart.read(me);
});
return await this.noteEntityService.packMany(timeline, me);
return await query.limit(ps.limit).getMany();
}
}

View file

@ -5,18 +5,17 @@
import { Inject, Injectable } from '@nestjs/common';
import { Brackets } from 'typeorm';
import type { MiNote, MiUserList, NotesRepository, UserListMembershipsRepository, UserListsRepository } from '@/models/_.js';
import type { MiUserList, NotesRepository, UserListMembershipsRepository, UserListsRepository } from '@/models/_.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import ActiveUsersChart from '@/core/chart/charts/active-users.js';
import { DI } from '@/di-symbols.js';
import { CacheService } from '@/core/CacheService.js';
import { IdService } from '@/core/IdService.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
import { QueryService } from '@/core/QueryService.js';
import { MiLocalUser } from '@/models/User.js';
import { MetaService } from '@/core/MetaService.js';
import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
import { ApiError } from '../../error.js';
export const meta = {
@ -52,6 +51,7 @@ export const paramDef = {
untilId: { type: 'string', format: 'misskey:id' },
sinceDate: { type: 'integer' },
untilDate: { type: 'integer' },
allowPartial: { type: 'boolean', default: false }, // true is recommended but for compatibility false by default
includeMyRenotes: { type: 'boolean', default: true },
includeRenotedMyNotes: { type: 'boolean', default: true },
includeLocalRenotes: { type: 'boolean', default: true },
@ -82,7 +82,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private activeUsersChart: ActiveUsersChart,
private cacheService: CacheService,
private idService: IdService,
private fanoutTimelineService: FanoutTimelineService,
private fanoutTimelineEndpointService: FanoutTimelineEndpointService,
private queryService: QueryService,
private metaService: MetaService,
) {
@ -102,7 +102,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
const serverSettings = await this.metaService.fetch();
if (!serverSettings.enableFanoutTimeline) {
return await this.getFromDb(list, {
const timeline = await this.getFromDb(list, {
untilId,
sinceId,
limit: ps.limit,
@ -113,74 +113,38 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
withRenotes: ps.withRenotes,
withCats: ps.withCats,
}, me);
}
const [
userIdsWhoMeMuting,
userIdsWhoMeMutingRenotes,
userIdsWhoBlockingMe,
] = await Promise.all([
this.cacheService.userMutingsCache.fetch(me.id),
this.cacheService.renoteMutingsCache.fetch(me.id),
this.cacheService.userBlockedCache.fetch(me.id),
]);
let noteIds = await this.fanoutTimelineService.get(ps.withFiles ? `userListTimelineWithFiles:${list.id}` : `userListTimeline:${list.id}`, untilId, sinceId);
noteIds = noteIds.slice(0, ps.limit);
let redisTimeline: MiNote[] = [];
if (noteIds.length > 0) {
const query = this.notesRepository.createQueryBuilder('note')
.where('note.id IN (:...noteIds)', { noteIds: noteIds })
.innerJoinAndSelect('note.user', 'user')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
.leftJoinAndSelect('renote.user', 'renoteUser')
.leftJoinAndSelect('note.channel', 'channel');
redisTimeline = await query.getMany();
redisTimeline = redisTimeline.filter(note => {
if (note.userId === me.id) {
return true;
}
if (isUserRelated(note, userIdsWhoBlockingMe)) return false;
if (isUserRelated(note, userIdsWhoMeMuting)) return false;
if (note.renoteId) {
if (note.text == null && note.fileIds.length === 0 && !note.hasPoll) {
if (isUserRelated(note, userIdsWhoMeMutingRenotes)) return false;
if (ps.withRenotes === false) return false;
}
}
return true;
});
redisTimeline.sort((a, b) => a.id > b.id ? -1 : 1);
}
if (redisTimeline.length > 0) {
this.activeUsersChart.read(me);
return await this.noteEntityService.packMany(redisTimeline, me);
} else {
if (serverSettings.enableFanoutTimelineDbFallback) { // fallback to db
return await this.getFromDb(list, {
untilId,
sinceId,
limit: ps.limit,
includeMyRenotes: ps.includeMyRenotes,
includeRenotedMyNotes: ps.includeRenotedMyNotes,
includeLocalRenotes: ps.includeLocalRenotes,
withFiles: ps.withFiles,
withRenotes: ps.withRenotes,
withCats: ps.withCats,
}, me);
} else {
return [];
}
await this.noteEntityService.packMany(timeline, me);
}
const timeline = await this.fanoutTimelineEndpointService.timeline({
untilId,
sinceId,
limit: ps.limit,
allowPartial: ps.allowPartial,
me,
useDbFallback: serverSettings.enableFanoutTimelineDbFallback,
redisTimelines: ps.withFiles ? [`userListTimelineWithFiles:${list.id}`] : [`userListTimeline:${list.id}`],
alwaysIncludeMyNotes: true,
excludePureRenotes: !ps.withRenotes,
dbFallback: async (untilId, sinceId, limit) => await this.getFromDb(list, {
untilId,
sinceId,
limit,
includeMyRenotes: ps.includeMyRenotes,
includeRenotedMyNotes: ps.includeRenotedMyNotes,
includeLocalRenotes: ps.includeLocalRenotes,
withFiles: ps.withFiles,
withRenotes: ps.withRenotes,
withCats: ps.withCats,
}, me),
});
this.activeUsersChart.read(me);
return timeline;
});
}
@ -279,10 +243,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}
//#endregion
const timeline = await query.limit(ps.limit).getMany();
this.activeUsersChart.read(me);
return await this.noteEntityService.packMany(timeline, me);
return await query.limit(ps.limit).getMany();
}
}

View file

@ -5,18 +5,18 @@
import { Brackets } from 'typeorm';
import { Inject, Injectable } from '@nestjs/common';
import * as Redis from 'ioredis';
import type { MiNote, NotesRepository } from '@/models/_.js';
import type { NotesRepository } from '@/models/_.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { DI } from '@/di-symbols.js';
import { CacheService } from '@/core/CacheService.js';
import { IdService } from '@/core/IdService.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { QueryService } from '@/core/QueryService.js';
import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
import { MetaService } from '@/core/MetaService.js';
import { ApiError } from '../../error.js';
import { MiLocalUser } from '@/models/User.js';
import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
import { FanoutTimelineName } from '@/core/FanoutTimelineService.js';
import { ApiError } from '@/server/api/error.js';
export const meta = {
tags: ['users', 'notes'],
@ -37,6 +37,12 @@ export const meta = {
code: 'NO_SUCH_USER',
id: '27e494ba-2ac2-48e8-893b-10d4d8c2387b',
},
bothWithRepliesAndWithFiles: {
message: 'Specifying both withReplies and withFiles is not supported',
code: 'BOTH_WITH_REPLIES_AND_WITH_FILES',
id: '91c8cb9f-36ed-46e7-9ca2-7df96ed6e222',
},
},
} as const;
@ -52,9 +58,9 @@ export const paramDef = {
untilId: { type: 'string', format: 'misskey:id' },
sinceDate: { type: 'integer' },
untilDate: { type: 'integer' },
allowPartial: { type: 'boolean', default: false }, // true is recommended but for compatibility false by default
withFiles: { type: 'boolean', default: false },
withCats: { type: 'boolean', default: false },
excludeNsfw: { type: 'boolean', default: false },
},
required: ['userId'],
} as const;
@ -62,9 +68,6 @@ export const paramDef = {
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
@Inject(DI.redisForTimelines)
private redisForTimelines: Redis.Redis,
@Inject(DI.notesRepository)
private notesRepository: NotesRepository,
@ -72,125 +75,129 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private queryService: QueryService,
private cacheService: CacheService,
private idService: IdService,
private fanoutTimelineService: FanoutTimelineService,
private fanoutTimelineEndpointService: FanoutTimelineEndpointService,
private metaService: MetaService,
) {
super(meta, paramDef, async (ps, me) => {
const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null);
const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : null);
const isRangeSpecified = untilId != null && sinceId != null;
const isSelf = me && (me.id === ps.userId);
const serverSettings = await this.metaService.fetch();
if (serverSettings.enableFanoutTimeline && (isRangeSpecified || sinceId == null)) {
const [
userIdsWhoMeMuting,
] = me ? await Promise.all([
this.cacheService.userMutingsCache.fetch(me.id),
]) : [new Set<string>()];
if (ps.withReplies && ps.withFiles) throw new ApiError(meta.errors.bothWithRepliesAndWithFiles);
const [noteIdsRes, repliesNoteIdsRes, channelNoteIdsRes] = await Promise.all([
this.fanoutTimelineService.get(ps.withFiles ? `userTimelineWithFiles:${ps.userId}` : `userTimeline:${ps.userId}`, untilId, sinceId),
ps.withReplies ? this.fanoutTimelineService.get(`userTimelineWithReplies:${ps.userId}`, untilId, sinceId) : Promise.resolve([]),
ps.withChannelNotes ? this.fanoutTimelineService.get(`userTimelineWithChannel:${ps.userId}`, untilId, sinceId) : Promise.resolve([]),
]);
if (!serverSettings.enableFanoutTimeline) {
const timeline = await this.getFromDb({
untilId,
sinceId,
limit: ps.limit,
userId: ps.userId,
withChannelNotes: ps.withChannelNotes,
withFiles: ps.withFiles,
withRenotes: ps.withRenotes,
withCats: ps.withCats,
}, me);
let noteIds = Array.from(new Set([
...noteIdsRes,
...repliesNoteIdsRes,
...channelNoteIdsRes,
]));
noteIds.sort((a, b) => a > b ? -1 : 1);
noteIds = noteIds.slice(0, ps.limit);
if (noteIds.length > 0) {
const isFollowing = me && Object.hasOwn(await this.cacheService.userFollowingsCache.fetch(me.id), ps.userId);
const query = this.notesRepository.createQueryBuilder('note')
.where('note.id IN (:...noteIds)', { noteIds: noteIds })
.innerJoinAndSelect('note.user', 'user')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
.leftJoinAndSelect('renote.user', 'renoteUser')
.leftJoinAndSelect('note.channel', 'channel');
if (ps.withCats) {
query.andWhere('(select "isCat" from "user" where id = note."userId")');
}
let timeline = await query.getMany();
timeline = timeline.filter(note => {
if (me && isUserRelated(note, userIdsWhoMeMuting, true)) return false;
if (note.renoteId) {
if (note.text == null && note.fileIds.length === 0 && !note.hasPoll) {
if (ps.withRenotes === false) return false;
}
}
if (note.channel?.isSensitive && !isSelf) return false;
if (note.visibility === 'specified' && (!me || (me.id !== note.userId && !note.visibleUserIds.some(v => v === me.id)))) return false;
if (note.visibility === 'followers' && !isFollowing && !isSelf) return false;
return true;
});
// TODO: フィルタで件数が減った場合の埋め合わせ処理
timeline.sort((a, b) => a.id > b.id ? -1 : 1);
if (timeline.length > 0) {
return await this.noteEntityService.packMany(timeline, me);
}
}
return await this.noteEntityService.packMany(timeline, me);
}
//#region fallback to database
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
.andWhere('note.userId = :userId', { userId: ps.userId })
.innerJoinAndSelect('note.user', 'user')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('note.channel', 'channel')
.leftJoinAndSelect('reply.user', 'replyUser')
.leftJoinAndSelect('renote.user', 'renoteUser');
const redisTimelines: FanoutTimelineName[] = [ps.withFiles ? `userTimelineWithFiles:${ps.userId}` : `userTimeline:${ps.userId}`];
if (ps.withChannelNotes) {
if (!isSelf) query.andWhere(new Brackets(qb => {
qb.orWhere('note.channelId IS NULL');
qb.orWhere('channel.isSensitive = false');
}));
} else {
query.andWhere('note.channelId IS NULL');
}
if (ps.withReplies) redisTimelines.push(`userTimelineWithReplies:${ps.userId}`);
if (ps.withChannelNotes) redisTimelines.push(`userTimelineWithChannel:${ps.userId}`);
this.queryService.generateVisibilityQuery(query, me);
if (me) {
this.queryService.generateMutedUserQuery(query, me, { id: ps.userId });
this.queryService.generateBlockedUserQuery(query, me);
}
const isFollowing = me && Object.hasOwn(await this.cacheService.userFollowingsCache.fetch(me.id), ps.userId);
if (ps.withFiles) {
query.andWhere('note.fileIds != \'{}\'');
}
const timeline = await this.fanoutTimelineEndpointService.timeline({
untilId,
sinceId,
limit: ps.limit,
allowPartial: ps.allowPartial,
me,
redisTimelines,
useDbFallback: true,
ignoreAuthorFromMute: true,
excludeReplies: ps.withChannelNotes && !ps.withReplies, // userTimelineWithChannel may include replies
excludeNoFiles: ps.withChannelNotes && ps.withFiles, // userTimelineWithChannel may include notes without files
excludePureRenotes: !ps.withRenotes,
noteFilter: note => {
if (note.channel?.isSensitive && !isSelf) return false;
if (note.visibility === 'specified' && (!me || (me.id !== note.userId && !note.visibleUserIds.some(v => v === me.id)))) return false;
if (note.visibility === 'followers' && !isFollowing && !isSelf) return false;
if (ps.withRenotes === false) {
query.andWhere(new Brackets(qb => {
qb.orWhere('note.userId != :userId', { userId: ps.userId });
qb.orWhere('note.renoteId IS NULL');
qb.orWhere('note.text IS NOT NULL');
qb.orWhere('note.fileIds != \'{}\'');
qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)');
}));
}
return true;
},
dbFallback: async (untilId, sinceId, limit) => await this.getFromDb({
untilId,
sinceId,
limit,
userId: ps.userId,
withChannelNotes: ps.withChannelNotes,
withFiles: ps.withFiles,
withRenotes: ps.withRenotes,
withCats: ps.withCats,
}, me),
});
const timeline = await query.limit(ps.limit).getMany();
return await this.noteEntityService.packMany(timeline, me);
//#endregion
return timeline;
});
}
private async getFromDb(ps: {
untilId: string | null,
sinceId: string | null,
limit: number,
userId: string,
withChannelNotes: boolean,
withFiles: boolean,
withCats: boolean,
withRenotes: boolean,
}, me: MiLocalUser | null) {
const isSelf = me && (me.id === ps.userId);
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId)
.andWhere('note.userId = :userId', { userId: ps.userId })
.innerJoinAndSelect('note.user', 'user')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('note.channel', 'channel')
.leftJoinAndSelect('reply.user', 'replyUser')
.leftJoinAndSelect('renote.user', 'renoteUser');
if (ps.withChannelNotes) {
if (!isSelf) query.andWhere(new Brackets(qb => {
qb.orWhere('note.channelId IS NULL');
qb.orWhere('channel.isSensitive = false');
}));
} else {
query.andWhere('note.channelId IS NULL');
}
this.queryService.generateVisibilityQuery(query, me);
if (me) {
this.queryService.generateMutedUserQuery(query, me, { id: ps.userId });
this.queryService.generateBlockedUserQuery(query, me);
}
if (ps.withFiles) {
query.andWhere('note.fileIds != \'{}\'');
}
if (ps.withRenotes === false) {
query.andWhere(new Brackets(qb => {
qb.orWhere('note.userId != :userId', { userId: ps.userId });
qb.orWhere('note.renoteId IS NULL');
qb.orWhere('note.text IS NOT NULL');
qb.orWhere('note.fileIds != \'{}\'');
qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)');
}));
}
if (ps.withCats) {
query.andWhere('(select "isCat" from "user" where id = note."userId")');
}
return await query.limit(ps.limit).getMany();
}
}

View file

@ -14,7 +14,6 @@ export function genOpenapiSpec(config: Config) {
info: {
version: config.version,
basedMisskeyVersion: config.basedMisskeyVersion,
title: 'CherryPick API',
'x-logo': { url: '/static-assets/api-doc.png' },
},

View file

@ -37,6 +37,7 @@ export default class Connection {
public userIdsWhoMeMuting: Set<string> = new Set();
public userIdsWhoBlockingMe: Set<string> = new Set();
public userIdsWhoMeMutingRenotes: Set<string> = new Set();
public userMutedInstances: Set<string> = new Set();
private fetchIntervalId: NodeJS.Timeout | null = null;
constructor(
@ -70,6 +71,7 @@ export default class Connection {
this.userIdsWhoMeMuting = userIdsWhoMeMuting;
this.userIdsWhoBlockingMe = userIdsWhoBlockingMe;
this.userIdsWhoMeMutingRenotes = userIdsWhoMeMutingRenotes;
this.userMutedInstances = new Set(userProfile.mutedInstances);
}
@bindThis

View file

@ -41,6 +41,10 @@ export default abstract class Channel {
return this.connection.userIdsWhoBlockingMe;
}
protected get userMutedInstances() {
return this.connection.userMutedInstances;
}
protected get followingChannels() {
return this.connection.followingChannels;
}

View file

@ -5,12 +5,12 @@
import { Inject, Injectable } from '@nestjs/common';
import type { MiUserListMembership, UserListMembershipsRepository, UserListsRepository } from '@/models/_.js';
import type { MiUser } from '@/models/User.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import type { Packed } from '@/misc/json-schema.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { isInstanceMuted } from '@/misc/is-instance-muted.js';
import Channel from '../channel.js';
class UserListChannel extends Channel {
@ -80,6 +80,9 @@ class UserListChannel extends Channel {
private async onNote(note: Packed<'Note'>) {
const isMe = this.user!.id === note.userId;
// チャンネル投稿は無視する
if (note.channelId) return;
if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return;
if (!Object.hasOwn(this.membershipsMap, note.userId)) return;
@ -115,6 +118,9 @@ class UserListChannel extends Channel {
}
}
// 流れてきたNoteがミュートしているインスタンスに関わるものだったら無視する
if (isInstanceMuted(note, this.userMutedInstances)) return;
this.connection.cacheNote(note);
this.send('note', note);

View file

@ -7,7 +7,7 @@ process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import { MiFollowing } from '@/models/Following.js';
import { connectStream, signup, api, post, startServer, initTestDb, waitFire } from '../utils.js';
import { signup, api, post, startServer, initTestDb, waitFire } from '../utils.js';
import type { INestApplicationContext } from '@nestjs/common';
import type * as misskey from 'cherrypick-js';
@ -34,12 +34,16 @@ describe('Streaming', () => {
let ayano: misskey.entities.MeSignup;
let kyoko: misskey.entities.MeSignup;
let chitose: misskey.entities.MeSignup;
let kanako: misskey.entities.MeSignup;
// Remote users
let akari: misskey.entities.MeSignup;
let chinatsu: misskey.entities.MeSignup;
let takumi: misskey.entities.MeSignup;
let kyokoNote: any;
let kanakoNote: any;
let takumiNote: any;
let list: any;
beforeAll(async () => {
@ -50,11 +54,15 @@ describe('Streaming', () => {
ayano = await signup({ username: 'ayano' });
kyoko = await signup({ username: 'kyoko' });
chitose = await signup({ username: 'chitose' });
kanako = await signup({ username: 'kanako' });
akari = await signup({ username: 'akari', host: 'example.com' });
chinatsu = await signup({ username: 'chinatsu', host: 'example.com' });
takumi = await signup({ username: 'takumi', host: 'example.com' });
kyokoNote = await post(kyoko, { text: 'foo' });
kanakoNote = await post(kanako, { text: 'hoge' });
takumiNote = await post(takumi, { text: 'piyo' });
// Follow: ayano => kyoko
await api('following/create', { userId: kyoko.id }, ayano);
@ -62,6 +70,9 @@ describe('Streaming', () => {
// Follow: ayano => akari
await follow(ayano, akari);
// Mute: chitose => kanako
await api('mute/create', { userId: kanako.id }, chitose);
// List: chitose => ayano, kyoko
list = await api('users/lists/create', {
name: 'my list',
@ -76,6 +87,11 @@ describe('Streaming', () => {
listId: list.id,
userId: kyoko.id,
}, chitose);
await api('users/lists/push', {
listId: list.id,
userId: takumi.id,
}, chitose);
}, 1000 * 60 * 2);
afterAll(async () => {
@ -452,6 +468,96 @@ describe('Streaming', () => {
assert.strictEqual(fired, false);
});
// #10443
test('チャンネル投稿は流れない', async () => {
// リスインしている kyoko が 任意のチャンネルに投降した時の動きを見たい
const fired = await waitFire(
chitose, 'userList',
() => api('notes/create', { text: 'foo', channelId: 'dummy' }, kyoko),
msg => msg.type === 'note' && msg.body.userId === kyoko.id,
{ listId: list.id },
);
assert.strictEqual(fired, false);
});
// #10443
test('ミュートしているユーザへのリプライがリストTLに流れない', async () => {
// chitose が kanako をミュートしている状態で、リスインしている kyoko が kanako にリプライした時の動きを見たい
const fired = await waitFire(
chitose, 'userList',
() => api('notes/create', { text: 'foo', replyId: kanakoNote.id }, kyoko),
msg => msg.type === 'note' && msg.body.userId === kyoko.id,
{ listId: list.id },
);
assert.strictEqual(fired, false);
});
// #10443
test('ミュートしているユーザの投稿をリートしたときリストTLに流れない', async () => {
// chitose が kanako をミュートしている状態で、リスインしている kyoko が kanako のノートをリノートした時の動きを見たい
const fired = await waitFire(
chitose, 'userList',
() => api('notes/create', { renoteId: kanakoNote.id }, kyoko),
msg => msg.type === 'note' && msg.body.userId === kyoko.id,
{ listId: list.id },
);
assert.strictEqual(fired, false);
});
// #10443
test('ミュートしているサーバのートがリストTLに流れない', async () => {
await api('/i/update', {
mutedInstances: ['example.com'],
}, chitose);
// chitose が example.com をミュートしている状態で、リスインしている takumi が ノートした時の動きを見たい
const fired = await waitFire(
chitose, 'userList',
() => api('notes/create', { text: 'foo' }, takumi),
msg => msg.type === 'note' && msg.body.userId === kyoko.id,
{ listId: list.id },
);
assert.strictEqual(fired, false);
});
// #10443
test('ミュートしているサーバのートに対するリプライがリストTLに流れない', async () => {
await api('/i/update', {
mutedInstances: ['example.com'],
}, chitose);
// chitose が example.com をミュートしている状態で、リスインしている kyoko が takumi のノートにリプライした時の動きを見たい
const fired = await waitFire(
chitose, 'userList',
() => api('notes/create', { text: 'foo', replyId: takumiNote.id }, kyoko),
msg => msg.type === 'note' && msg.body.userId === kyoko.id,
{ listId: list.id },
);
assert.strictEqual(fired, false);
});
// #10443
test('ミュートしているサーバのートに対するリートがリストTLに流れない', async () => {
await api('/i/update', {
mutedInstances: ['example.com'],
}, chitose);
// chitose が example.com をミュートしている状態で、リスインしている kyoko が takumi のノートをリノートした時の動きを見たい
const fired = await waitFire(
chitose, 'userList',
() => api('notes/create', { renoteId: takumiNote.id }, kyoko),
msg => msg.type === 'note' && msg.body.userId === kyoko.id,
{ listId: list.id },
);
assert.strictEqual(fired, false);
});
});
// XXX: QueryFailedError: duplicate key value violates unique constraint "IDX_347fec870eafea7b26c8a73bac"

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,9 @@
module.exports = {
parserOptions: {
tsconfigRootDir: __dirname,
project: ['./tsconfig.json'],
},
extends: [
'../../shared/.eslintrc.js',
],
};

View file

@ -0,0 +1 @@
api.json

View file

@ -0,0 +1,19 @@
## cherrypick-js向け型生成モジュール
バックエンドが吐き出すOpenAPI準拠のapi.jsonからcherrypick-jsで使用される型エイリアスを生成するためのモジュールです。
このモジュールはcherrypick-jsそのものにバンドルされることは想定しておらず、生成物をcherrypick-jsのsrc配下にコピーして使用することを想定しています。
## 使い方
まず、CherryPickのバックエンドからapi.jsonを取得する必要があります。任意のCherryPickインスタンスの/api-docからダウンロードしても良いですし、
backendモジュール配下で`pnpm generate-api-json`を実行しても良いでしょう。
api.jsonを入手したら、このファイルがあるディレクトリに置いてください。
その後、以下コマンドを実行します。
```shell
pnpm generate
```
上記を実行することで、`./built`ディレクトリ配下にtsファイルが生成されます。

View file

@ -0,0 +1,24 @@
{
"name": "cherrypick-js-type-generator",
"version": "0.0.0",
"description": "CherryPick TypeGenerator",
"type": "module",
"scripts": {
"generate": "tsx src/generator.ts && eslint ./built/**/* --ext .ts --fix"
},
"devDependencies": {
"@apidevtools/swagger-parser": "10.1.0",
"@types/node": "20.9.1",
"@typescript-eslint/eslint-plugin": "6.11.0",
"@typescript-eslint/parser": "6.11.0",
"eslint": "8.53.0",
"typescript": "5.3.2",
"tsx": "4.4.0",
"ts-case-convert": "2.0.2",
"openapi-types": "12.1.3",
"openapi-typescript": "6.7.1"
},
"files": [
"built"
]
}

View file

@ -0,0 +1,284 @@
import { mkdir, writeFile } from 'fs/promises';
import { OpenAPIV3 } from 'openapi-types';
import { toPascal } from 'ts-case-convert';
import SwaggerParser from '@apidevtools/swagger-parser';
import openapiTS from 'openapi-typescript';
function generateVersionHeaderComment(openApiDocs: OpenAPIV3.Document): string {
const contents = {
version: openApiDocs.info.version,
generatedAt: new Date().toISOString(),
};
const lines: string[] = [];
lines.push('/*');
for (const [key, value] of Object.entries(contents)) {
lines.push(` * ${key}: ${value}`);
}
lines.push(' */');
return lines.join('\n');
}
async function generateBaseTypes(
openApiDocs: OpenAPIV3.Document,
openApiJsonPath: string,
typeFileName: string,
) {
const disabledLints = [
'@typescript-eslint/naming-convention',
'@typescript-eslint/no-explicit-any',
];
const lines: string[] = [];
for (const lint of disabledLints) {
lines.push(`/* eslint ${lint}: 0 */`);
}
lines.push('');
lines.push(generateVersionHeaderComment(openApiDocs));
lines.push('');
const generatedTypes = await openapiTS(openApiJsonPath, { exportType: true });
lines.push(generatedTypes);
lines.push('');
await writeFile(typeFileName, lines.join('\n'));
}
async function generateSchemaEntities(
openApiDocs: OpenAPIV3.Document,
typeFileName: string,
outputPath: string,
) {
if (!openApiDocs.components?.schemas) {
return;
}
const schemas = openApiDocs.components.schemas;
const schemaNames = Object.keys(schemas);
const typeAliasLines: string[] = [];
typeAliasLines.push(generateVersionHeaderComment(openApiDocs));
typeAliasLines.push('');
typeAliasLines.push(`import { components } from '${toImportPath(typeFileName)}';`);
typeAliasLines.push(
...schemaNames.map(it => `export type ${it} = components['schemas']['${it}'];`),
);
typeAliasLines.push('');
await writeFile(outputPath, typeAliasLines.join('\n'));
}
async function generateEndpoints(
openApiDocs: OpenAPIV3.Document,
typeFileName: string,
entitiesOutputPath: string,
endpointOutputPath: string,
) {
const endpoints: Endpoint[] = [];
// cherrypick-jsはPOST固定で送っているので、こちらも決め打ちする。別メソッドに対応することがあればこちらも直す必要あり
const paths = openApiDocs.paths;
const postPathItems = Object.keys(paths)
.map(it => paths[it]?.post)
.filter(filterUndefined);
for (const operation of postPathItems) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const operationId = operation.operationId!;
const endpoint = new Endpoint(operationId);
endpoints.push(endpoint);
if (isRequestBodyObject(operation.requestBody)) {
const reqContent = operation.requestBody.content;
const supportMediaTypes = Object.keys(reqContent);
if (supportMediaTypes.length > 0) {
// いまのところ複数のメディアタイプをとるエンドポイントは無いので決め打ちする
endpoint.request = new OperationTypeAlias(
operationId,
supportMediaTypes[0],
OperationsAliasType.REQUEST,
);
}
}
if (isResponseObject(operation.responses['200']) && operation.responses['200'].content) {
const resContent = operation.responses['200'].content;
const supportMediaTypes = Object.keys(resContent);
if (supportMediaTypes.length > 0) {
// いまのところ複数のメディアタイプを返すエンドポイントは無いので決め打ちする
endpoint.response = new OperationTypeAlias(
operationId,
supportMediaTypes[0],
OperationsAliasType.RESPONSE,
);
}
}
}
const entitiesOutputLine: string[] = [];
entitiesOutputLine.push(generateVersionHeaderComment(openApiDocs));
entitiesOutputLine.push('');
entitiesOutputLine.push(`import { operations } from '${toImportPath(typeFileName)}';`);
entitiesOutputLine.push('');
entitiesOutputLine.push(new EmptyTypeAlias(OperationsAliasType.REQUEST).toLine());
entitiesOutputLine.push(new EmptyTypeAlias(OperationsAliasType.RESPONSE).toLine());
entitiesOutputLine.push('');
const entities = endpoints
.flatMap(it => [it.request, it.response].filter(i => i))
.filter(filterUndefined);
entitiesOutputLine.push(...entities.map(it => it.toLine()));
entitiesOutputLine.push('');
await writeFile(entitiesOutputPath, entitiesOutputLine.join('\n'));
const endpointOutputLine: string[] = [];
endpointOutputLine.push(generateVersionHeaderComment(openApiDocs));
endpointOutputLine.push('');
endpointOutputLine.push('import type {');
endpointOutputLine.push(
...[emptyRequest, emptyResponse, ...entities].map(it => '\t' + it.generateName() + ','),
);
endpointOutputLine.push(`} from '${toImportPath(entitiesOutputPath)}';`);
endpointOutputLine.push('');
endpointOutputLine.push('export type Endpoints = {');
endpointOutputLine.push(
...endpoints.map(it => '\t' + it.toLine()),
);
endpointOutputLine.push('}');
endpointOutputLine.push('');
await writeFile(endpointOutputPath, endpointOutputLine.join('\n'));
}
function isRequestBodyObject(value: unknown): value is OpenAPIV3.RequestBodyObject {
if (!value) {
return false;
}
const { content } = value as Record<keyof OpenAPIV3.RequestBodyObject, unknown>;
return content !== undefined;
}
function isResponseObject(value: unknown): value is OpenAPIV3.ResponseObject {
if (!value) {
return false;
}
const { description } = value as Record<keyof OpenAPIV3.ResponseObject, unknown>;
return description !== undefined;
}
function filterUndefined<T>(item: T): item is Exclude<T, undefined> {
return item !== undefined;
}
function toImportPath(fileName: string, fromPath = '/built/autogen', toPath = ''): string {
return fileName.replace(fromPath, toPath).replace('.ts', '.js');
}
enum OperationsAliasType {
REQUEST = 'Request',
RESPONSE = 'Response'
}
interface IOperationTypeAlias {
readonly type: OperationsAliasType
generateName(): string
toLine(): string
}
class OperationTypeAlias implements IOperationTypeAlias {
public readonly operationId: string;
public readonly mediaType: string;
public readonly type: OperationsAliasType;
constructor(
operationId: string,
mediaType: string,
type: OperationsAliasType,
) {
this.operationId = operationId;
this.mediaType = mediaType;
this.type = type;
}
generateName(): string {
const nameBase = this.operationId.replace(/\//g, '-');
return toPascal(nameBase + this.type);
}
toLine(): string {
const name = this.generateName();
return (this.type === OperationsAliasType.REQUEST)
? `export type ${name} = operations['${this.operationId}']['requestBody']['content']['${this.mediaType}'];`
: `export type ${name} = operations['${this.operationId}']['responses']['200']['content']['${this.mediaType}'];`;
}
}
class EmptyTypeAlias implements IOperationTypeAlias {
readonly type: OperationsAliasType;
constructor(type: OperationsAliasType) {
this.type = type;
}
generateName(): string {
return 'Empty' + this.type;
}
toLine(): string {
const name = this.generateName();
return `export type ${name} = Record<string, unknown> | undefined;`;
}
}
const emptyRequest = new EmptyTypeAlias(OperationsAliasType.REQUEST);
const emptyResponse = new EmptyTypeAlias(OperationsAliasType.RESPONSE);
class Endpoint {
public readonly operationId: string;
public request?: IOperationTypeAlias;
public response?: IOperationTypeAlias;
constructor(operationId: string) {
this.operationId = operationId;
}
toLine(): string {
const reqName = this.request?.generateName() ?? emptyRequest.generateName();
const resName = this.response?.generateName() ?? emptyResponse.generateName();
return `'${this.operationId}': { req: ${reqName}; res: ${resName} };`;
}
}
async function main() {
const generatePath = './built/autogen';
await mkdir(generatePath, { recursive: true });
const openApiJsonPath = './api.json';
const openApiDocs = await SwaggerParser.validate(openApiJsonPath) as OpenAPIV3.Document;
const typeFileName = './built/autogen/types.ts';
await generateBaseTypes(openApiDocs, openApiJsonPath, typeFileName);
const modelFileName = `${generatePath}/models.ts`;
await generateSchemaEntities(openApiDocs, typeFileName, modelFileName);
const entitiesFileName = `${generatePath}/entities.ts`;
const endpointFileName = `${generatePath}/endpoint.ts`;
await generateEndpoints(openApiDocs, typeFileName, entitiesFileName, endpointFileName);
}
main();

View file

@ -0,0 +1,20 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "nodenext",
"strict": true,
"strictFunctionTypes": true,
"strictNullChecks": true,
"esModuleInterop": true,
"lib": [
"esnext",
]
},
"include": [
"src/**/*.ts",
"built/**/*.ts"
],
"exclude": []
}

View file

@ -13,7 +13,8 @@
"typecheck": "tsc --noEmit",
"lint": "pnpm typecheck && pnpm eslint",
"jest": "jest --coverage --detectOpenHandles",
"test": "pnpm jest && pnpm tsd"
"test": "pnpm jest && pnpm tsd",
"update-autogen-code": "pnpm --filter cherrypick-js-type-generator generate && ncp generator/built/autogen src/autogen"
},
"repository": {
"type": "git",
@ -32,7 +33,8 @@
"jest-websocket-mock": "2.5.0",
"mock-socket": "9.3.1",
"tsd": "0.29.0",
"typescript": "5.3.2"
"typescript": "5.3.2",
"ncp": "2.0.0"
},
"files": [
"built"

View file

@ -1,4 +1,9 @@
import type { Endpoints } from './api.types.js';
import { SwitchCaseResponseType } from './api.types';
import type { Endpoints } from './api.types';
export {
SwitchCaseResponseType,
} from './api.types';
const MK_API_ERROR = Symbol();
@ -15,25 +20,15 @@ export function isAPIError(reason: any): reason is APIError {
}
export type FetchLike = (input: string, init?: {
method?: string;
body?: string;
credentials?: RequestCredentials;
cache?: RequestCache;
headers: {[key in string]: string}
}) => Promise<{
status: number;
json(): Promise<any>;
}>;
type IsNeverType<T> = [T] extends [never] ? true : false;
type StrictExtract<Union, Cond> = Cond extends Union ? Union : never;
type IsCaseMatched<E extends keyof Endpoints, P extends Endpoints[E]['req'], C extends number> =
IsNeverType<StrictExtract<Endpoints[E]['res']['$switch']['$cases'][C], [P, any]>> extends false ? true : false;
type GetCaseResult<E extends keyof Endpoints, P extends Endpoints[E]['req'], C extends number> =
StrictExtract<Endpoints[E]['res']['$switch']['$cases'][C], [P, any]>[1];
method?: string;
body?: string;
credentials?: RequestCredentials;
cache?: RequestCache;
headers: { [key in string]: string }
}) => Promise<{
status: number;
json(): Promise<any>;
}>;
export class APIClient {
public origin: string;
@ -53,22 +48,11 @@ export class APIClient {
}
public request<E extends keyof Endpoints, P extends Endpoints[E]['req']>(
endpoint: E, params: P = {} as P, credential?: string | null | undefined,
): Promise<Endpoints[E]['res'] extends { $switch: { $cases: [any, any][]; $default: any; }; }
?
IsCaseMatched<E, P, 0> extends true ? GetCaseResult<E, P, 0> :
IsCaseMatched<E, P, 1> extends true ? GetCaseResult<E, P, 1> :
IsCaseMatched<E, P, 2> extends true ? GetCaseResult<E, P, 2> :
IsCaseMatched<E, P, 3> extends true ? GetCaseResult<E, P, 3> :
IsCaseMatched<E, P, 4> extends true ? GetCaseResult<E, P, 4> :
IsCaseMatched<E, P, 5> extends true ? GetCaseResult<E, P, 5> :
IsCaseMatched<E, P, 6> extends true ? GetCaseResult<E, P, 6> :
IsCaseMatched<E, P, 7> extends true ? GetCaseResult<E, P, 7> :
IsCaseMatched<E, P, 8> extends true ? GetCaseResult<E, P, 8> :
IsCaseMatched<E, P, 9> extends true ? GetCaseResult<E, P, 9> :
Endpoints[E]['res']['$switch']['$default']
: Endpoints[E]['res']> {
const promise = new Promise((resolve, reject) => {
endpoint: E,
params: P = {} as P,
credential?: string | null,
): Promise<SwitchCaseResponseType<E, P>> {
return new Promise((resolve, reject) => {
this.fetch(`${this.origin}/api/${endpoint}`, {
method: 'POST',
body: JSON.stringify({
@ -83,10 +67,8 @@ export class APIClient {
}).then(async (res) => {
const body = res.status === 204 ? null : await res.json();
if (res.status === 200) {
if (res.status === 200 || res.status === 204) {
resolve(body);
} else if (res.status === 204) {
resolve(null);
} else {
reject({
[MK_API_ERROR]: true,
@ -95,7 +77,5 @@ export class APIClient {
}
}).catch(reject);
});
return promise as any;
}
}

View file

@ -1,681 +1,60 @@
import type {
Ad, Announcement, Antenna, App, AuthSession, Blocking, Channel, Clip, DateString, DetailedInstanceMetadata, DriveFile, DriveFolder, Following, FollowingFolloweePopulated, FollowingFollowerPopulated, FollowRequest, GalleryPost, Instance,
LiteInstanceMetadata,
MeDetailed,
Note, NoteFavorite, OriginType, Page, ServerInfo, Stats, User, UserDetailed, MeSignup, UserGroup, UserList, UserSorting, Notification, NoteReaction, Signin, MessagingMessage, Invite, InviteLimit, AdminInstanceMetadata,
} from './entities.js';
import { Endpoints as Gen } from './autogen/endpoint';
import { UserDetailed } from './autogen/models';
import { UsersShowRequest } from './autogen/entities';
type TODO = Record<string, any> | null;
type Overwrite<T, U extends { [Key in keyof T]?: unknown }> = Omit<
T,
keyof U
> & U;
type NoParams = Record<string, never>;
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/unset-user-avatar': { req: { userId: User['id']; }; res: null; };
'admin/unset-user-banner': { 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/meta': { req: NoParams; res: AdminInstanceMetadata; };
'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/abuse-report-resolver/create': { req: TODO; res: TODO; };
'admin/abuse-report-resolver/list': { req: TODO; res: TODO; };
'admin/abuse-report-resolver/update': { req: TODO; res: TODO; };
'admin/abuse-report-resolver/delete': { 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/adds': { 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/steal': { 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/invite/create': { req: TODO; res: TODO; };
'admin/invite/list': { req: TODO; res: TODO; };
'admin/invite/revoke': { req: TODO; res: TODO; };
'admin/invite/revoke-unused': { 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[]; };
// 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; };
// ap
'ap/get': { req: { uri: string; }; res: Record<string, any>; };
'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; };
// 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 }; };
// 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; };
// charts
'charts/active-users': { req: { span: 'day' | 'hour'; limit?: number; offset?: number | null; }; res: {
local: {
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/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[];
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; };
// 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: {
folderId?: string,
name?: string,
comment?: string,
isSentisive?: boolean,
force?: boolean,
};
res: DriveFile;
};
'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; }[]; }; };
// endpoints
'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[]; };
// following
'following/create': { req: {
userId: User['id'],
withReplies?: boolean,
}; 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: null; res: GalleryPost[]; };
'gallery/popular': { req: null; res: GalleryPost[]; };
'gallery/posts': { req: { limit?: number; sinceId?: GalleryPost['id']; untilId?: GalleryPost['id']; }; res: GalleryPost[]; };
'gallery/posts/create': { req: { title: GalleryPost['title']; description?: GalleryPost['description']; fileIds: GalleryPost['fileIds']; isSensitive?: GalleryPost['isSensitive'] }; res: GalleryPost; };
'gallery/posts/delete': { req: { postId: GalleryPost['id'] }; res: null; };
'gallery/posts/like': { req: { postId: GalleryPost['id'] }; res: null; };
'gallery/posts/show': { req: { postId: GalleryPost['id'] }; res: GalleryPost; };
'gallery/posts/unlike': { req: { postId: GalleryPost['id'] }; res: null; };
'gallery/posts/update': { req: { postId: GalleryPost['id']; title: GalleryPost['title']; description?: GalleryPost['description']; fileIds: GalleryPost['fileIds']; isSensitive?: GalleryPost['isSensitive'] }; res: GalleryPost; };
// 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; };
// get-online-users-count
'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; };
// 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/import-following': { req: TODO; res: TODO; };
'i/import-user-lists': { req: TODO; res: TODO; };
'i/move': { 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<string, any>; };
'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/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[] | string)[];
hardMutedWords?: (string[] | string)[];
notificationRecieveConfig?: any;
emailNotificationTypes?: string[];
alsoKnownAs?: 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; };
// flash
'flash/gen-token': { req: TODO; res: TODO; };
// invite
'invite/create': { req: NoParams; res: Invite; };
'invite/delete': { req: { inviteId: Invite['id']; }; res: null; };
'invite/list': { req: { limit?: number; sinceId?: Invite['id']; untilId?: Invite['id'] }; res: Invite[]; };
'invite/limit': { req: NoParams; res: InviteLimit; };
// 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; };
// meta
'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; };
// mute
'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; };
// 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;
};
event?: null | {
title: string;
start: number;
end?: null | number;
metadata: Record<string, string>;
}
}; res: { createdNote: Note }; };
'notes/delete': { req: { noteId: Note['id']; }; res: null; };
'notes/update': { req: { noteId: Note['id']; text?: null | string; cw?: null | string; }; 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/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/events/search': { req: {
query?: string;
sinceId?: Note['id'];
untilId?: Note['id'];
limit?: number;
offset?: number;
users?: User['id'][];
sinceDate?: number;
untilDate?: number;
sortBy?: 'startDate' | 'craetedAt';
filters?: { key: string[], values: (string | null)[] }[];
}; res: Note[]; };
'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/test-notification': { req: NoParams; res: null; };
'notifications/mark-all-as-read': { req: NoParams; res: null; };
// page-push
'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; };
// ping
'ping': { req: NoParams; res: { pong: number; }; };
// pinned-users
'pinned-users': { req: TODO; res: TODO; };
// promo
'promo/read': { req: TODO; res: TODO; };
// request-reset-password
'request-reset-password': { req: { username: string; email: string; }; res: null; };
// reset-password
'reset-password': { req: { token: string; password: string; }; res: null; };
// room
'room/show': { req: TODO; res: TODO; };
'room/update': { req: TODO; res: TODO; };
// signup
'signup': {
req: {
username: string;
password: string;
host?: string;
invitationCode?: string;
emailAddress?: string;
'hcaptcha-response'?: string;
'g-recaptcha-response'?: string;
'turnstile-response'?: string;
};
res: MeSignup | null;
};
// stats
'stats': { req: NoParams; res: Stats; };
// server-info
'server-info': { req: NoParams; res: ServerInfo; };
// sw
'sw/register': { req: TODO; res: TODO; };
// username
'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/flashs': { 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; };
// fetching external data
'fetch-rss': { req: { url: string; }; res: TODO; };
'fetch-external-resources': {
req: { url: string; hash: string; };
res: { type: string; data: string; };
type SwitchCase = {
$switch: {
$cases: [any, any][],
$default: any;
};
};
type IsNeverType<T> = [T] extends [never] ? true : false;
type StrictExtract<Union, Cond> = Cond extends Union ? Union : never;
type IsCaseMatched<E extends keyof Endpoints, P extends Endpoints[E]['req'], C extends number> =
Endpoints[E]['res'] extends SwitchCase
? IsNeverType<StrictExtract<Endpoints[E]['res']['$switch']['$cases'][C], [P, any]>> extends false ? true : false
: false
type GetCaseResult<E extends keyof Endpoints, P extends Endpoints[E]['req'], C extends number> =
Endpoints[E]['res'] extends SwitchCase
? StrictExtract<Endpoints[E]['res']['$switch']['$cases'][C], [P, any]>[1]
: never
export type SwitchCaseResponseType<E extends keyof Endpoints, P extends Endpoints[E]['req']> = Endpoints[E]['res'] extends SwitchCase
? IsCaseMatched<E, P, 0> extends true ? GetCaseResult<E, P, 0> :
IsCaseMatched<E, P, 1> extends true ? GetCaseResult<E, P, 1> :
IsCaseMatched<E, P, 2> extends true ? GetCaseResult<E, P, 2> :
IsCaseMatched<E, P, 3> extends true ? GetCaseResult<E, P, 3> :
IsCaseMatched<E, P, 4> extends true ? GetCaseResult<E, P, 4> :
IsCaseMatched<E, P, 5> extends true ? GetCaseResult<E, P, 5> :
IsCaseMatched<E, P, 6> extends true ? GetCaseResult<E, P, 6> :
IsCaseMatched<E, P, 7> extends true ? GetCaseResult<E, P, 7> :
IsCaseMatched<E, P, 8> extends true ? GetCaseResult<E, P, 8> :
IsCaseMatched<E, P, 9> extends true ? GetCaseResult<E, P, 9> :
Endpoints[E]['res']['$switch']['$default'] : Endpoints[E]['res'];
export type Endpoints = Overwrite<
Gen,
{
'users/show': {
req: UsersShowRequest;
res: {
$switch: {
$cases: [[
{
userIds?: string[];
}, UserDetailed[],
]];
$default: UserDetailed;
};
};
}
}
>

View file

@ -0,0 +1,874 @@
/*
* version: 4.6.0-beta.1
* generatedAt: 2023-12-05T08:05:02.075Z
*/
import type {
EmptyRequest,
EmptyResponse,
AdminMetaResponse,
AdminAbuseReportResolverCreateRequest,
AdminAbuseReportResolverCreateResponse,
AdminAbuseReportResolverListRequest,
AdminAbuseReportResolverListResponse,
AdminAbuseReportResolverDeleteRequest,
AdminAbuseReportResolverUpdateRequest,
AdminAbuseUserReportsRequest,
AdminAbuseUserReportsResponse,
AdminAccountsCreateRequest,
AdminAccountsCreateResponse,
AdminAccountsDeleteRequest,
AdminAccountsFindByEmailRequest,
AdminAdCreateRequest,
AdminAdDeleteRequest,
AdminAdListRequest,
AdminAdUpdateRequest,
AdminAnnouncementsCreateRequest,
AdminAnnouncementsCreateResponse,
AdminAnnouncementsDeleteRequest,
AdminAnnouncementsListRequest,
AdminAnnouncementsListResponse,
AdminAnnouncementsUpdateRequest,
AdminAvatarDecorationsCreateRequest,
AdminAvatarDecorationsDeleteRequest,
AdminAvatarDecorationsListRequest,
AdminAvatarDecorationsListResponse,
AdminAvatarDecorationsUpdateRequest,
AdminDeleteAllFilesOfAUserRequest,
AdminUnsetUserAvatarRequest,
AdminUnsetUserBannerRequest,
AdminDriveFilesRequest,
AdminDriveFilesResponse,
AdminDriveShowFileRequest,
AdminDriveShowFileResponse,
AdminEmojiAddAliasesBulkRequest,
AdminEmojiAddRequest,
AdminEmojiAddsRequest,
AdminEmojiCopyRequest,
AdminEmojiCopyResponse,
AdminEmojiDeleteBulkRequest,
AdminEmojiDeleteRequest,
AdminEmojiListRemoteRequest,
AdminEmojiListRemoteResponse,
AdminEmojiListRequest,
AdminEmojiListResponse,
AdminEmojiRemoveAliasesBulkRequest,
AdminEmojiSetAliasesBulkRequest,
AdminEmojiSetCategoryBulkRequest,
AdminEmojiSetLicenseBulkRequest,
AdminEmojiStealRequest,
AdminEmojiStealResponse,
AdminEmojiUpdateRequest,
AdminFederationDeleteAllFilesRequest,
AdminFederationRefreshRemoteInstanceMetadataRequest,
AdminFederationRemoveAllFollowingRequest,
AdminFederationUpdateInstanceRequest,
AdminGetTableStatsResponse,
AdminGetUserIpsRequest,
AdminInviteCreateRequest,
AdminInviteCreateResponse,
AdminInviteListRequest,
AdminInviteListResponse,
AdminPromoCreateRequest,
AdminQueueDeliverDelayedResponse,
AdminQueueInboxDelayedResponse,
AdminQueuePromoteRequest,
AdminQueueStatsResponse,
AdminRelaysAddRequest,
AdminRelaysAddResponse,
AdminRelaysListResponse,
AdminRelaysRemoveRequest,
AdminResetPasswordRequest,
AdminResetPasswordResponse,
AdminResolveAbuseUserReportRequest,
AdminSendEmailRequest,
AdminServerInfoResponse,
AdminShowModerationLogsRequest,
AdminShowModerationLogsResponse,
AdminShowUserRequest,
AdminShowUserResponse,
AdminShowUsersRequest,
AdminShowUsersResponse,
AdminSuspendUserRequest,
AdminUnsuspendUserRequest,
AdminUpdateMetaRequest,
AdminDeleteAccountRequest,
AdminDeleteAccountResponse,
AdminUpdateUserNoteRequest,
AdminRolesCreateRequest,
AdminRolesDeleteRequest,
AdminRolesShowRequest,
AdminRolesUpdateRequest,
AdminRolesAssignRequest,
AdminRolesUnassignRequest,
AdminRolesUpdateDefaultPoliciesRequest,
AdminRolesUsersRequest,
AnnouncementsRequest,
AnnouncementsResponse,
AntennasCreateRequest,
AntennasCreateResponse,
AntennasDeleteRequest,
AntennasListResponse,
AntennasNotesRequest,
AntennasNotesResponse,
AntennasShowRequest,
AntennasShowResponse,
AntennasUpdateRequest,
AntennasUpdateResponse,
ApGetRequest,
ApGetResponse,
ApShowRequest,
ApShowResponse,
AppCreateRequest,
AppCreateResponse,
AppShowRequest,
AppShowResponse,
AuthSessionGenerateRequest,
AuthSessionGenerateResponse,
AuthSessionShowRequest,
AuthSessionShowResponse,
AuthSessionUserkeyRequest,
AuthSessionUserkeyResponse,
BlockingCreateRequest,
BlockingCreateResponse,
BlockingDeleteRequest,
BlockingDeleteResponse,
BlockingListRequest,
BlockingListResponse,
ChannelsCreateRequest,
ChannelsCreateResponse,
ChannelsFeaturedResponse,
ChannelsFollowRequest,
ChannelsFollowedRequest,
ChannelsFollowedResponse,
ChannelsOwnedRequest,
ChannelsOwnedResponse,
ChannelsShowRequest,
ChannelsShowResponse,
ChannelsTimelineRequest,
ChannelsTimelineResponse,
ChannelsUnfollowRequest,
ChannelsUpdateRequest,
ChannelsUpdateResponse,
ChannelsFavoriteRequest,
ChannelsUnfavoriteRequest,
ChannelsMyFavoritesResponse,
ChannelsSearchRequest,
ChannelsSearchResponse,
ChartsActiveUsersRequest,
ChartsActiveUsersResponse,
ChartsApRequestRequest,
ChartsApRequestResponse,
ChartsDriveRequest,
ChartsDriveResponse,
ChartsFederationRequest,
ChartsFederationResponse,
ChartsInstanceRequest,
ChartsInstanceResponse,
ChartsNotesRequest,
ChartsNotesResponse,
ChartsUserDriveRequest,
ChartsUserDriveResponse,
ChartsUserFollowingRequest,
ChartsUserFollowingResponse,
ChartsUserNotesRequest,
ChartsUserNotesResponse,
ChartsUserPvRequest,
ChartsUserPvResponse,
ChartsUserReactionsRequest,
ChartsUserReactionsResponse,
ChartsUsersRequest,
ChartsUsersResponse,
ClipsAddNoteRequest,
ClipsRemoveNoteRequest,
ClipsCreateRequest,
ClipsCreateResponse,
ClipsDeleteRequest,
ClipsListResponse,
ClipsNotesRequest,
ClipsNotesResponse,
ClipsShowRequest,
ClipsShowResponse,
ClipsUpdateRequest,
ClipsUpdateResponse,
ClipsFavoriteRequest,
ClipsUnfavoriteRequest,
ClipsMyFavoritesResponse,
DriveResponse,
DriveFilesRequest,
DriveFilesResponse,
DriveFilesAttachedNotesRequest,
DriveFilesAttachedNotesResponse,
DriveFilesCheckExistenceRequest,
DriveFilesCheckExistenceResponse,
DriveFilesCreateRequest,
DriveFilesCreateResponse,
DriveFilesDeleteRequest,
DriveFilesFindByHashRequest,
DriveFilesFindByHashResponse,
DriveFilesFindRequest,
DriveFilesFindResponse,
DriveFilesShowRequest,
DriveFilesShowResponse,
DriveFilesUpdateRequest,
DriveFilesUpdateResponse,
DriveFilesUploadFromUrlRequest,
DriveFoldersRequest,
DriveFoldersResponse,
DriveFoldersCreateRequest,
DriveFoldersCreateResponse,
DriveFoldersDeleteRequest,
DriveFoldersFindRequest,
DriveFoldersFindResponse,
DriveFoldersShowRequest,
DriveFoldersShowResponse,
DriveFoldersUpdateRequest,
DriveFoldersUpdateResponse,
DriveStreamRequest,
DriveStreamResponse,
EmailAddressAvailableRequest,
EmailAddressAvailableResponse,
EndpointRequest,
EndpointsResponse,
FederationFollowersRequest,
FederationFollowersResponse,
FederationFollowingRequest,
FederationFollowingResponse,
FederationInstancesRequest,
FederationInstancesResponse,
FederationShowInstanceRequest,
FederationShowInstanceResponse,
FederationUpdateRemoteUserRequest,
FederationUsersRequest,
FederationUsersResponse,
FederationStatsRequest,
FollowingCreateRequest,
FollowingCreateResponse,
FollowingDeleteRequest,
FollowingDeleteResponse,
FollowingUpdateRequest,
FollowingUpdateResponse,
FollowingUpdateAllRequest,
FollowingInvalidateRequest,
FollowingInvalidateResponse,
FollowingRequestsAcceptRequest,
FollowingRequestsCancelRequest,
FollowingRequestsCancelResponse,
FollowingRequestsListRequest,
FollowingRequestsListResponse,
FollowingRequestsRejectRequest,
GalleryFeaturedRequest,
GalleryFeaturedResponse,
GalleryPopularResponse,
GalleryPostsRequest,
GalleryPostsResponse,
GalleryPostsCreateRequest,
GalleryPostsCreateResponse,
GalleryPostsDeleteRequest,
GalleryPostsLikeRequest,
GalleryPostsShowRequest,
GalleryPostsShowResponse,
GalleryPostsUnlikeRequest,
GalleryPostsUpdateRequest,
GalleryPostsUpdateResponse,
GetAvatarDecorationsResponse,
HashtagsListRequest,
HashtagsListResponse,
HashtagsSearchRequest,
HashtagsSearchResponse,
HashtagsShowRequest,
HashtagsShowResponse,
HashtagsTrendResponse,
HashtagsUsersRequest,
HashtagsUsersResponse,
IResponse,
IClaimAchievementRequest,
IFavoritesRequest,
IFavoritesResponse,
IGalleryLikesRequest,
IGalleryLikesResponse,
IGalleryPostsRequest,
IGalleryPostsResponse,
INotificationsRequest,
INotificationsResponse,
INotificationsGroupedRequest,
INotificationsGroupedResponse,
IPageLikesRequest,
IPageLikesResponse,
IPagesRequest,
IPagesResponse,
IPinRequest,
IPinResponse,
IReadAnnouncementRequest,
IRegistryGetAllRequest,
IRegistryGetDetailRequest,
IRegistryGetRequest,
IRegistryKeysWithTypeRequest,
IRegistryKeysRequest,
IRegistryRemoveRequest,
IRegistrySetRequest,
IUnpinRequest,
IUnpinResponse,
IUpdateRequest,
IUpdateResponse,
IUserGroupInvitesRequest,
IUserGroupInvitesResponse,
IWebhooksCreateRequest,
IWebhooksShowRequest,
IWebhooksUpdateRequest,
IWebhooksDeleteRequest,
InviteCreateResponse,
InviteDeleteRequest,
InviteListRequest,
InviteListResponse,
InviteLimitResponse,
MessagingHistoryRequest,
MessagingHistoryResponse,
MessagingMessagesRequest,
MessagingMessagesResponse,
MessagingMessagesCreateRequest,
MessagingMessagesCreateResponse,
MessagingMessagesDeleteRequest,
MessagingMessagesReadRequest,
MetaRequest,
MetaResponse,
EmojisResponse,
EmojiRequest,
EmojiResponse,
MuteCreateRequest,
MuteDeleteRequest,
MuteListRequest,
MuteListResponse,
RenoteMuteCreateRequest,
RenoteMuteDeleteRequest,
RenoteMuteListRequest,
RenoteMuteListResponse,
MyAppsRequest,
MyAppsResponse,
NotesRequest,
NotesResponse,
NotesChildrenRequest,
NotesChildrenResponse,
NotesClipsRequest,
NotesClipsResponse,
NotesConversationRequest,
NotesConversationResponse,
NotesCreateRequest,
NotesCreateResponse,
NotesDeleteRequest,
NotesUpdateRequest,
NotesFavoritesCreateRequest,
NotesFavoritesDeleteRequest,
NotesFeaturedRequest,
NotesFeaturedResponse,
NotesGlobalTimelineRequest,
NotesGlobalTimelineResponse,
NotesHybridTimelineRequest,
NotesHybridTimelineResponse,
NotesLocalTimelineRequest,
NotesLocalTimelineResponse,
NotesMentionsRequest,
NotesMentionsResponse,
NotesPollsRecommendationRequest,
NotesPollsRecommendationResponse,
NotesPollsVoteRequest,
NotesEventsSearchRequest,
NotesEventsSearchResponse,
NotesReactionsRequest,
NotesReactionsResponse,
NotesReactionsCreateRequest,
NotesReactionsDeleteRequest,
NotesRenotesRequest,
NotesRenotesResponse,
NotesRepliesRequest,
NotesRepliesResponse,
NotesSearchByTagRequest,
NotesSearchByTagResponse,
NotesSearchRequest,
NotesSearchResponse,
NotesShowRequest,
NotesShowResponse,
NotesStateRequest,
NotesStateResponse,
NotesThreadMutingCreateRequest,
NotesThreadMutingDeleteRequest,
NotesTimelineRequest,
NotesTimelineResponse,
NotesTranslateRequest,
NotesTranslateResponse,
NotesUnrenoteRequest,
NotesUserListTimelineRequest,
NotesUserListTimelineResponse,
NotificationsCreateRequest,
PagesCreateRequest,
PagesCreateResponse,
PagesDeleteRequest,
PagesFeaturedResponse,
PagesLikeRequest,
PagesShowRequest,
PagesShowResponse,
PagesUnlikeRequest,
PagesUpdateRequest,
FlashCreateRequest,
FlashDeleteRequest,
FlashFeaturedResponse,
FlashLikeRequest,
FlashShowRequest,
FlashShowResponse,
FlashUnlikeRequest,
FlashUpdateRequest,
FlashMyRequest,
FlashMyResponse,
FlashMyLikesRequest,
FlashMyLikesResponse,
PingResponse,
PinnedUsersResponse,
PromoReadRequest,
RolesShowRequest,
RolesUsersRequest,
RolesNotesRequest,
RolesNotesResponse,
RequestResetPasswordRequest,
ResetPasswordRequest,
StatsResponse,
SwShowRegistrationRequest,
SwShowRegistrationResponse,
SwUpdateRegistrationRequest,
SwUpdateRegistrationResponse,
SwRegisterRequest,
SwRegisterResponse,
SwUnregisterRequest,
TestRequest,
UsernameAvailableRequest,
UsernameAvailableResponse,
UsersRequest,
UsersResponse,
UsersClipsRequest,
UsersClipsResponse,
UsersFollowersRequest,
UsersFollowersResponse,
UsersFollowingRequest,
UsersFollowingResponse,
UsersGalleryPostsRequest,
UsersGalleryPostsResponse,
UsersGetFrequentlyRepliedUsersRequest,
UsersGetFrequentlyRepliedUsersResponse,
UsersFeaturedNotesRequest,
UsersFeaturedNotesResponse,
UsersGroupsCreateRequest,
UsersGroupsCreateResponse,
UsersGroupsDeleteRequest,
UsersGroupsInvitationsAcceptRequest,
UsersGroupsInvitationsRejectRequest,
UsersGroupsInviteRequest,
UsersGroupsJoinedResponse,
UsersGroupsLeaveRequest,
UsersGroupsOwnedResponse,
UsersGroupsPullRequest,
UsersGroupsShowRequest,
UsersGroupsShowResponse,
UsersGroupsTransferRequest,
UsersGroupsTransferResponse,
UsersGroupsUpdateRequest,
UsersGroupsUpdateResponse,
UsersListsCreateRequest,
UsersListsCreateResponse,
UsersListsDeleteRequest,
UsersListsListRequest,
UsersListsListResponse,
UsersListsPullRequest,
UsersListsPushRequest,
UsersListsShowRequest,
UsersListsShowResponse,
UsersListsFavoriteRequest,
UsersListsUnfavoriteRequest,
UsersListsUpdateRequest,
UsersListsUpdateResponse,
UsersListsCreateFromPublicRequest,
UsersListsCreateFromPublicResponse,
UsersListsUpdateMembershipRequest,
UsersListsGetMembershipsRequest,
UsersNotesRequest,
UsersNotesResponse,
UsersPagesRequest,
UsersPagesResponse,
UsersFlashsRequest,
UsersFlashsResponse,
UsersReactionsRequest,
UsersReactionsResponse,
UsersRecommendationRequest,
UsersRecommendationResponse,
UsersRelationRequest,
UsersRelationResponse,
UsersReportAbuseRequest,
UsersSearchByUsernameAndHostRequest,
UsersSearchByUsernameAndHostResponse,
UsersSearchRequest,
UsersSearchResponse,
UsersShowRequest,
UsersShowResponse,
UsersStatsRequest,
UsersStatsResponse,
UsersAchievementsRequest,
UsersUpdateMemoRequest,
UsersTranslateRequest,
UsersTranslateResponse,
FetchRssRequest,
FetchExternalResourcesRequest,
RetentionResponse,
} from './entities.js';
export type Endpoints = {
'admin/meta': { req: EmptyRequest; res: AdminMetaResponse };
'admin/abuse-report-resolver/create': { req: AdminAbuseReportResolverCreateRequest; res: AdminAbuseReportResolverCreateResponse };
'admin/abuse-report-resolver/list': { req: AdminAbuseReportResolverListRequest; res: AdminAbuseReportResolverListResponse };
'admin/abuse-report-resolver/delete': { req: AdminAbuseReportResolverDeleteRequest; res: EmptyResponse };
'admin/abuse-report-resolver/update': { req: AdminAbuseReportResolverUpdateRequest; res: EmptyResponse };
'admin/abuse-user-reports': { req: AdminAbuseUserReportsRequest; res: AdminAbuseUserReportsResponse };
'admin/accounts/create': { req: AdminAccountsCreateRequest; res: AdminAccountsCreateResponse };
'admin/accounts/delete': { req: AdminAccountsDeleteRequest; res: EmptyResponse };
'admin/accounts/find-by-email': { req: AdminAccountsFindByEmailRequest; res: EmptyResponse };
'admin/ad/create': { req: AdminAdCreateRequest; res: EmptyResponse };
'admin/ad/delete': { req: AdminAdDeleteRequest; res: EmptyResponse };
'admin/ad/list': { req: AdminAdListRequest; res: EmptyResponse };
'admin/ad/update': { req: AdminAdUpdateRequest; res: EmptyResponse };
'admin/announcements/create': { req: AdminAnnouncementsCreateRequest; res: AdminAnnouncementsCreateResponse };
'admin/announcements/delete': { req: AdminAnnouncementsDeleteRequest; res: EmptyResponse };
'admin/announcements/list': { req: AdminAnnouncementsListRequest; res: AdminAnnouncementsListResponse };
'admin/announcements/update': { req: AdminAnnouncementsUpdateRequest; res: EmptyResponse };
'admin/avatar-decorations/create': { req: AdminAvatarDecorationsCreateRequest; res: EmptyResponse };
'admin/avatar-decorations/delete': { req: AdminAvatarDecorationsDeleteRequest; res: EmptyResponse };
'admin/avatar-decorations/list': { req: AdminAvatarDecorationsListRequest; res: AdminAvatarDecorationsListResponse };
'admin/avatar-decorations/update': { req: AdminAvatarDecorationsUpdateRequest; res: EmptyResponse };
'admin/delete-all-files-of-a-user': { req: AdminDeleteAllFilesOfAUserRequest; res: EmptyResponse };
'admin/unset-user-avatar': { req: AdminUnsetUserAvatarRequest; res: EmptyResponse };
'admin/unset-user-banner': { req: AdminUnsetUserBannerRequest; res: EmptyResponse };
'admin/drive/clean-remote-files': { req: EmptyRequest; res: EmptyResponse };
'admin/drive/cleanup': { req: EmptyRequest; res: EmptyResponse };
'admin/drive/files': { req: AdminDriveFilesRequest; res: AdminDriveFilesResponse };
'admin/drive/show-file': { req: AdminDriveShowFileRequest; res: AdminDriveShowFileResponse };
'admin/emoji/add-aliases-bulk': { req: AdminEmojiAddAliasesBulkRequest; res: EmptyResponse };
'admin/emoji/add': { req: AdminEmojiAddRequest; res: EmptyResponse };
'admin/emoji/adds': { req: AdminEmojiAddsRequest; res: EmptyResponse };
'admin/emoji/copy': { req: AdminEmojiCopyRequest; res: AdminEmojiCopyResponse };
'admin/emoji/delete-bulk': { req: AdminEmojiDeleteBulkRequest; res: EmptyResponse };
'admin/emoji/delete': { req: AdminEmojiDeleteRequest; res: EmptyResponse };
'admin/emoji/list-remote': { req: AdminEmojiListRemoteRequest; res: AdminEmojiListRemoteResponse };
'admin/emoji/list': { req: AdminEmojiListRequest; res: AdminEmojiListResponse };
'admin/emoji/remove-aliases-bulk': { req: AdminEmojiRemoveAliasesBulkRequest; res: EmptyResponse };
'admin/emoji/set-aliases-bulk': { req: AdminEmojiSetAliasesBulkRequest; res: EmptyResponse };
'admin/emoji/set-category-bulk': { req: AdminEmojiSetCategoryBulkRequest; res: EmptyResponse };
'admin/emoji/set-license-bulk': { req: AdminEmojiSetLicenseBulkRequest; res: EmptyResponse };
'admin/emoji/steal': { req: AdminEmojiStealRequest; res: AdminEmojiStealResponse };
'admin/emoji/update': { req: AdminEmojiUpdateRequest; res: EmptyResponse };
'admin/federation/delete-all-files': { req: AdminFederationDeleteAllFilesRequest; res: EmptyResponse };
'admin/federation/refresh-remote-instance-metadata': { req: AdminFederationRefreshRemoteInstanceMetadataRequest; res: EmptyResponse };
'admin/federation/remove-all-following': { req: AdminFederationRemoveAllFollowingRequest; res: EmptyResponse };
'admin/federation/update-instance': { req: AdminFederationUpdateInstanceRequest; res: EmptyResponse };
'admin/get-index-stats': { req: EmptyRequest; res: EmptyResponse };
'admin/get-table-stats': { req: EmptyRequest; res: AdminGetTableStatsResponse };
'admin/get-user-ips': { req: AdminGetUserIpsRequest; res: EmptyResponse };
'admin/invite/create': { req: AdminInviteCreateRequest; res: AdminInviteCreateResponse };
'admin/invite/list': { req: AdminInviteListRequest; res: AdminInviteListResponse };
'admin/invite/revoke': { req: EmptyRequest; res: EmptyResponse };
'admin/promo/create': { req: AdminPromoCreateRequest; res: EmptyResponse };
'admin/queue/clear': { req: EmptyRequest; res: EmptyResponse };
'admin/queue/deliver-delayed': { req: EmptyRequest; res: AdminQueueDeliverDelayedResponse };
'admin/queue/inbox-delayed': { req: EmptyRequest; res: AdminQueueInboxDelayedResponse };
'admin/queue/promote': { req: AdminQueuePromoteRequest; res: EmptyResponse };
'admin/queue/stats': { req: EmptyRequest; res: AdminQueueStatsResponse };
'admin/relays/add': { req: AdminRelaysAddRequest; res: AdminRelaysAddResponse };
'admin/relays/list': { req: EmptyRequest; res: AdminRelaysListResponse };
'admin/relays/remove': { req: AdminRelaysRemoveRequest; res: EmptyResponse };
'admin/reset-password': { req: AdminResetPasswordRequest; res: AdminResetPasswordResponse };
'admin/resolve-abuse-user-report': { req: AdminResolveAbuseUserReportRequest; res: EmptyResponse };
'admin/send-email': { req: AdminSendEmailRequest; res: EmptyResponse };
'admin/server-info': { req: EmptyRequest; res: AdminServerInfoResponse };
'admin/show-moderation-logs': { req: AdminShowModerationLogsRequest; res: AdminShowModerationLogsResponse };
'admin/show-user': { req: AdminShowUserRequest; res: AdminShowUserResponse };
'admin/show-users': { req: AdminShowUsersRequest; res: AdminShowUsersResponse };
'admin/suspend-user': { req: AdminSuspendUserRequest; res: EmptyResponse };
'admin/unsuspend-user': { req: AdminUnsuspendUserRequest; res: EmptyResponse };
'admin/update-meta': { req: AdminUpdateMetaRequest; res: EmptyResponse };
'admin/delete-account': { req: AdminDeleteAccountRequest; res: AdminDeleteAccountResponse };
'admin/update-user-note': { req: AdminUpdateUserNoteRequest; res: EmptyResponse };
'admin/roles/create': { req: AdminRolesCreateRequest; res: EmptyResponse };
'admin/roles/delete': { req: AdminRolesDeleteRequest; res: EmptyResponse };
'admin/roles/list': { req: EmptyRequest; res: EmptyResponse };
'admin/roles/show': { req: AdminRolesShowRequest; res: EmptyResponse };
'admin/roles/update': { req: AdminRolesUpdateRequest; res: EmptyResponse };
'admin/roles/assign': { req: AdminRolesAssignRequest; res: EmptyResponse };
'admin/roles/unassign': { req: AdminRolesUnassignRequest; res: EmptyResponse };
'admin/roles/update-default-policies': { req: AdminRolesUpdateDefaultPoliciesRequest; res: EmptyResponse };
'admin/roles/users': { req: AdminRolesUsersRequest; res: EmptyResponse };
'announcements': { req: AnnouncementsRequest; res: AnnouncementsResponse };
'antennas/create': { req: AntennasCreateRequest; res: AntennasCreateResponse };
'antennas/delete': { req: AntennasDeleteRequest; res: EmptyResponse };
'antennas/list': { req: EmptyRequest; res: AntennasListResponse };
'antennas/notes': { req: AntennasNotesRequest; res: AntennasNotesResponse };
'antennas/show': { req: AntennasShowRequest; res: AntennasShowResponse };
'antennas/update': { req: AntennasUpdateRequest; res: AntennasUpdateResponse };
'ap/get': { req: ApGetRequest; res: ApGetResponse };
'ap/show': { req: ApShowRequest; res: ApShowResponse };
'app/create': { req: AppCreateRequest; res: AppCreateResponse };
'app/show': { req: AppShowRequest; res: AppShowResponse };
'auth/session/generate': { req: AuthSessionGenerateRequest; res: AuthSessionGenerateResponse };
'auth/session/show': { req: AuthSessionShowRequest; res: AuthSessionShowResponse };
'auth/session/userkey': { req: AuthSessionUserkeyRequest; res: AuthSessionUserkeyResponse };
'blocking/create': { req: BlockingCreateRequest; res: BlockingCreateResponse };
'blocking/delete': { req: BlockingDeleteRequest; res: BlockingDeleteResponse };
'blocking/list': { req: BlockingListRequest; res: BlockingListResponse };
'channels/create': { req: ChannelsCreateRequest; res: ChannelsCreateResponse };
'channels/featured': { req: EmptyRequest; res: ChannelsFeaturedResponse };
'channels/follow': { req: ChannelsFollowRequest; res: EmptyResponse };
'channels/followed': { req: ChannelsFollowedRequest; res: ChannelsFollowedResponse };
'channels/owned': { req: ChannelsOwnedRequest; res: ChannelsOwnedResponse };
'channels/show': { req: ChannelsShowRequest; res: ChannelsShowResponse };
'channels/timeline': { req: ChannelsTimelineRequest; res: ChannelsTimelineResponse };
'channels/unfollow': { req: ChannelsUnfollowRequest; res: EmptyResponse };
'channels/update': { req: ChannelsUpdateRequest; res: ChannelsUpdateResponse };
'channels/favorite': { req: ChannelsFavoriteRequest; res: EmptyResponse };
'channels/unfavorite': { req: ChannelsUnfavoriteRequest; res: EmptyResponse };
'channels/my-favorites': { req: EmptyRequest; res: ChannelsMyFavoritesResponse };
'channels/search': { req: ChannelsSearchRequest; res: ChannelsSearchResponse };
'charts/active-users': { req: ChartsActiveUsersRequest; res: ChartsActiveUsersResponse };
'charts/ap-request': { req: ChartsApRequestRequest; res: ChartsApRequestResponse };
'charts/drive': { req: ChartsDriveRequest; res: ChartsDriveResponse };
'charts/federation': { req: ChartsFederationRequest; res: ChartsFederationResponse };
'charts/instance': { req: ChartsInstanceRequest; res: ChartsInstanceResponse };
'charts/notes': { req: ChartsNotesRequest; res: ChartsNotesResponse };
'charts/user/drive': { req: ChartsUserDriveRequest; res: ChartsUserDriveResponse };
'charts/user/following': { req: ChartsUserFollowingRequest; res: ChartsUserFollowingResponse };
'charts/user/notes': { req: ChartsUserNotesRequest; res: ChartsUserNotesResponse };
'charts/user/pv': { req: ChartsUserPvRequest; res: ChartsUserPvResponse };
'charts/user/reactions': { req: ChartsUserReactionsRequest; res: ChartsUserReactionsResponse };
'charts/users': { req: ChartsUsersRequest; res: ChartsUsersResponse };
'clips/add-note': { req: ClipsAddNoteRequest; res: EmptyResponse };
'clips/remove-note': { req: ClipsRemoveNoteRequest; res: EmptyResponse };
'clips/create': { req: ClipsCreateRequest; res: ClipsCreateResponse };
'clips/delete': { req: ClipsDeleteRequest; res: EmptyResponse };
'clips/list': { req: EmptyRequest; res: ClipsListResponse };
'clips/notes': { req: ClipsNotesRequest; res: ClipsNotesResponse };
'clips/show': { req: ClipsShowRequest; res: ClipsShowResponse };
'clips/update': { req: ClipsUpdateRequest; res: ClipsUpdateResponse };
'clips/favorite': { req: ClipsFavoriteRequest; res: EmptyResponse };
'clips/unfavorite': { req: ClipsUnfavoriteRequest; res: EmptyResponse };
'clips/my-favorites': { req: EmptyRequest; res: ClipsMyFavoritesResponse };
'drive': { req: EmptyRequest; res: DriveResponse };
'drive/files': { req: DriveFilesRequest; res: DriveFilesResponse };
'drive/files/attached-notes': { req: DriveFilesAttachedNotesRequest; res: DriveFilesAttachedNotesResponse };
'drive/files/check-existence': { req: DriveFilesCheckExistenceRequest; res: DriveFilesCheckExistenceResponse };
'drive/files/create': { req: DriveFilesCreateRequest; res: DriveFilesCreateResponse };
'drive/files/delete': { req: DriveFilesDeleteRequest; res: EmptyResponse };
'drive/files/find-by-hash': { req: DriveFilesFindByHashRequest; res: DriveFilesFindByHashResponse };
'drive/files/find': { req: DriveFilesFindRequest; res: DriveFilesFindResponse };
'drive/files/show': { req: DriveFilesShowRequest; res: DriveFilesShowResponse };
'drive/files/update': { req: DriveFilesUpdateRequest; res: DriveFilesUpdateResponse };
'drive/files/upload-from-url': { req: DriveFilesUploadFromUrlRequest; res: EmptyResponse };
'drive/folders': { req: DriveFoldersRequest; res: DriveFoldersResponse };
'drive/folders/create': { req: DriveFoldersCreateRequest; res: DriveFoldersCreateResponse };
'drive/folders/delete': { req: DriveFoldersDeleteRequest; res: EmptyResponse };
'drive/folders/find': { req: DriveFoldersFindRequest; res: DriveFoldersFindResponse };
'drive/folders/show': { req: DriveFoldersShowRequest; res: DriveFoldersShowResponse };
'drive/folders/update': { req: DriveFoldersUpdateRequest; res: DriveFoldersUpdateResponse };
'drive/stream': { req: DriveStreamRequest; res: DriveStreamResponse };
'email-address/available': { req: EmailAddressAvailableRequest; res: EmailAddressAvailableResponse };
'endpoint': { req: EndpointRequest; res: EmptyResponse };
'endpoints': { req: EmptyRequest; res: EndpointsResponse };
'federation/followers': { req: FederationFollowersRequest; res: FederationFollowersResponse };
'federation/following': { req: FederationFollowingRequest; res: FederationFollowingResponse };
'federation/instances': { req: FederationInstancesRequest; res: FederationInstancesResponse };
'federation/show-instance': { req: FederationShowInstanceRequest; res: FederationShowInstanceResponse };
'federation/update-remote-user': { req: FederationUpdateRemoteUserRequest; res: EmptyResponse };
'federation/users': { req: FederationUsersRequest; res: FederationUsersResponse };
'federation/stats': { req: FederationStatsRequest; res: EmptyResponse };
'following/create': { req: FollowingCreateRequest; res: FollowingCreateResponse };
'following/delete': { req: FollowingDeleteRequest; res: FollowingDeleteResponse };
'following/update': { req: FollowingUpdateRequest; res: FollowingUpdateResponse };
'following/update-all': { req: FollowingUpdateAllRequest; res: EmptyResponse };
'following/invalidate': { req: FollowingInvalidateRequest; res: FollowingInvalidateResponse };
'following/requests/accept': { req: FollowingRequestsAcceptRequest; res: EmptyResponse };
'following/requests/cancel': { req: FollowingRequestsCancelRequest; res: FollowingRequestsCancelResponse };
'following/requests/list': { req: FollowingRequestsListRequest; res: FollowingRequestsListResponse };
'following/requests/reject': { req: FollowingRequestsRejectRequest; res: EmptyResponse };
'gallery/featured': { req: GalleryFeaturedRequest; res: GalleryFeaturedResponse };
'gallery/popular': { req: EmptyRequest; res: GalleryPopularResponse };
'gallery/posts': { req: GalleryPostsRequest; res: GalleryPostsResponse };
'gallery/posts/create': { req: GalleryPostsCreateRequest; res: GalleryPostsCreateResponse };
'gallery/posts/delete': { req: GalleryPostsDeleteRequest; res: EmptyResponse };
'gallery/posts/like': { req: GalleryPostsLikeRequest; res: EmptyResponse };
'gallery/posts/show': { req: GalleryPostsShowRequest; res: GalleryPostsShowResponse };
'gallery/posts/unlike': { req: GalleryPostsUnlikeRequest; res: EmptyResponse };
'gallery/posts/update': { req: GalleryPostsUpdateRequest; res: GalleryPostsUpdateResponse };
'get-online-users-count': { req: EmptyRequest; res: EmptyResponse };
'get-avatar-decorations': { req: EmptyRequest; res: GetAvatarDecorationsResponse };
'hashtags/list': { req: HashtagsListRequest; res: HashtagsListResponse };
'hashtags/search': { req: HashtagsSearchRequest; res: HashtagsSearchResponse };
'hashtags/show': { req: HashtagsShowRequest; res: HashtagsShowResponse };
'hashtags/trend': { req: EmptyRequest; res: HashtagsTrendResponse };
'hashtags/users': { req: HashtagsUsersRequest; res: HashtagsUsersResponse };
'i': { req: EmptyRequest; res: IResponse };
'i/claim-achievement': { req: IClaimAchievementRequest; res: EmptyResponse };
'i/favorites': { req: IFavoritesRequest; res: IFavoritesResponse };
'i/gallery/likes': { req: IGalleryLikesRequest; res: IGalleryLikesResponse };
'i/gallery/posts': { req: IGalleryPostsRequest; res: IGalleryPostsResponse };
'i/notifications': { req: INotificationsRequest; res: INotificationsResponse };
'i/notifications-grouped': { req: INotificationsGroupedRequest; res: INotificationsGroupedResponse };
'i/page-likes': { req: IPageLikesRequest; res: IPageLikesResponse };
'i/pages': { req: IPagesRequest; res: IPagesResponse };
'i/pin': { req: IPinRequest; res: IPinResponse };
'i/read-all-messaging-messages': { req: EmptyRequest; res: EmptyResponse };
'i/read-all-unread-notes': { req: EmptyRequest; res: EmptyResponse };
'i/read-announcement': { req: IReadAnnouncementRequest; res: EmptyResponse };
'i/registry/get-all': { req: IRegistryGetAllRequest; res: EmptyResponse };
'i/registry/get-detail': { req: IRegistryGetDetailRequest; res: EmptyResponse };
'i/registry/get': { req: IRegistryGetRequest; res: EmptyResponse };
'i/registry/keys-with-type': { req: IRegistryKeysWithTypeRequest; res: EmptyResponse };
'i/registry/keys': { req: IRegistryKeysRequest; res: EmptyResponse };
'i/registry/remove': { req: IRegistryRemoveRequest; res: EmptyResponse };
'i/registry/set': { req: IRegistrySetRequest; res: EmptyResponse };
'i/unpin': { req: IUnpinRequest; res: IUnpinResponse };
'i/update': { req: IUpdateRequest; res: IUpdateResponse };
'i/user-group-invites': { req: IUserGroupInvitesRequest; res: IUserGroupInvitesResponse };
'i/webhooks/create': { req: IWebhooksCreateRequest; res: EmptyResponse };
'i/webhooks/list': { req: EmptyRequest; res: EmptyResponse };
'i/webhooks/show': { req: IWebhooksShowRequest; res: EmptyResponse };
'i/webhooks/update': { req: IWebhooksUpdateRequest; res: EmptyResponse };
'i/webhooks/delete': { req: IWebhooksDeleteRequest; res: EmptyResponse };
'invite/create': { req: EmptyRequest; res: InviteCreateResponse };
'invite/delete': { req: InviteDeleteRequest; res: EmptyResponse };
'invite/list': { req: InviteListRequest; res: InviteListResponse };
'invite/limit': { req: EmptyRequest; res: InviteLimitResponse };
'messaging/history': { req: MessagingHistoryRequest; res: MessagingHistoryResponse };
'messaging/messages': { req: MessagingMessagesRequest; res: MessagingMessagesResponse };
'messaging/messages/create': { req: MessagingMessagesCreateRequest; res: MessagingMessagesCreateResponse };
'messaging/messages/delete': { req: MessagingMessagesDeleteRequest; res: EmptyResponse };
'messaging/messages/read': { req: MessagingMessagesReadRequest; res: EmptyResponse };
'meta': { req: MetaRequest; res: MetaResponse };
'emojis': { req: EmptyRequest; res: EmojisResponse };
'emoji': { req: EmojiRequest; res: EmojiResponse };
'mute/create': { req: MuteCreateRequest; res: EmptyResponse };
'mute/delete': { req: MuteDeleteRequest; res: EmptyResponse };
'mute/list': { req: MuteListRequest; res: MuteListResponse };
'renote-mute/create': { req: RenoteMuteCreateRequest; res: EmptyResponse };
'renote-mute/delete': { req: RenoteMuteDeleteRequest; res: EmptyResponse };
'renote-mute/list': { req: RenoteMuteListRequest; res: RenoteMuteListResponse };
'my/apps': { req: MyAppsRequest; res: MyAppsResponse };
'notes': { req: NotesRequest; res: NotesResponse };
'notes/children': { req: NotesChildrenRequest; res: NotesChildrenResponse };
'notes/clips': { req: NotesClipsRequest; res: NotesClipsResponse };
'notes/conversation': { req: NotesConversationRequest; res: NotesConversationResponse };
'notes/create': { req: NotesCreateRequest; res: NotesCreateResponse };
'notes/delete': { req: NotesDeleteRequest; res: EmptyResponse };
'notes/update': { req: NotesUpdateRequest; res: EmptyResponse };
'notes/favorites/create': { req: NotesFavoritesCreateRequest; res: EmptyResponse };
'notes/favorites/delete': { req: NotesFavoritesDeleteRequest; res: EmptyResponse };
'notes/featured': { req: NotesFeaturedRequest; res: NotesFeaturedResponse };
'notes/global-timeline': { req: NotesGlobalTimelineRequest; res: NotesGlobalTimelineResponse };
'notes/hybrid-timeline': { req: NotesHybridTimelineRequest; res: NotesHybridTimelineResponse };
'notes/local-timeline': { req: NotesLocalTimelineRequest; res: NotesLocalTimelineResponse };
'notes/mentions': { req: NotesMentionsRequest; res: NotesMentionsResponse };
'notes/polls/recommendation': { req: NotesPollsRecommendationRequest; res: NotesPollsRecommendationResponse };
'notes/polls/vote': { req: NotesPollsVoteRequest; res: EmptyResponse };
'notes/events/search': { req: NotesEventsSearchRequest; res: NotesEventsSearchResponse };
'notes/reactions': { req: NotesReactionsRequest; res: NotesReactionsResponse };
'notes/reactions/create': { req: NotesReactionsCreateRequest; res: EmptyResponse };
'notes/reactions/delete': { req: NotesReactionsDeleteRequest; res: EmptyResponse };
'notes/renotes': { req: NotesRenotesRequest; res: NotesRenotesResponse };
'notes/replies': { req: NotesRepliesRequest; res: NotesRepliesResponse };
'notes/search-by-tag': { req: NotesSearchByTagRequest; res: NotesSearchByTagResponse };
'notes/search': { req: NotesSearchRequest; res: NotesSearchResponse };
'notes/show': { req: NotesShowRequest; res: NotesShowResponse };
'notes/state': { req: NotesStateRequest; res: NotesStateResponse };
'notes/thread-muting/create': { req: NotesThreadMutingCreateRequest; res: EmptyResponse };
'notes/thread-muting/delete': { req: NotesThreadMutingDeleteRequest; res: EmptyResponse };
'notes/timeline': { req: NotesTimelineRequest; res: NotesTimelineResponse };
'notes/translate': { req: NotesTranslateRequest; res: NotesTranslateResponse };
'notes/unrenote': { req: NotesUnrenoteRequest; res: EmptyResponse };
'notes/user-list-timeline': { req: NotesUserListTimelineRequest; res: NotesUserListTimelineResponse };
'notifications/create': { req: NotificationsCreateRequest; res: EmptyResponse };
'notifications/mark-all-as-read': { req: EmptyRequest; res: EmptyResponse };
'notifications/test-notification': { req: EmptyRequest; res: EmptyResponse };
'pages/create': { req: PagesCreateRequest; res: PagesCreateResponse };
'pages/delete': { req: PagesDeleteRequest; res: EmptyResponse };
'pages/featured': { req: EmptyRequest; res: PagesFeaturedResponse };
'pages/like': { req: PagesLikeRequest; res: EmptyResponse };
'pages/show': { req: PagesShowRequest; res: PagesShowResponse };
'pages/unlike': { req: PagesUnlikeRequest; res: EmptyResponse };
'pages/update': { req: PagesUpdateRequest; res: EmptyResponse };
'flash/create': { req: FlashCreateRequest; res: EmptyResponse };
'flash/delete': { req: FlashDeleteRequest; res: EmptyResponse };
'flash/featured': { req: EmptyRequest; res: FlashFeaturedResponse };
'flash/like': { req: FlashLikeRequest; res: EmptyResponse };
'flash/show': { req: FlashShowRequest; res: FlashShowResponse };
'flash/unlike': { req: FlashUnlikeRequest; res: EmptyResponse };
'flash/update': { req: FlashUpdateRequest; res: EmptyResponse };
'flash/my': { req: FlashMyRequest; res: FlashMyResponse };
'flash/my-likes': { req: FlashMyLikesRequest; res: FlashMyLikesResponse };
'ping': { req: EmptyRequest; res: PingResponse };
'pinned-users': { req: EmptyRequest; res: PinnedUsersResponse };
'promo/read': { req: PromoReadRequest; res: EmptyResponse };
'roles/list': { req: EmptyRequest; res: EmptyResponse };
'roles/show': { req: RolesShowRequest; res: EmptyResponse };
'roles/users': { req: RolesUsersRequest; res: EmptyResponse };
'roles/notes': { req: RolesNotesRequest; res: RolesNotesResponse };
'request-reset-password': { req: RequestResetPasswordRequest; res: EmptyResponse };
'reset-db': { req: EmptyRequest; res: EmptyResponse };
'reset-password': { req: ResetPasswordRequest; res: EmptyResponse };
'server-info': { req: EmptyRequest; res: EmptyResponse };
'stats': { req: EmptyRequest; res: StatsResponse };
'sw/show-registration': { req: SwShowRegistrationRequest; res: SwShowRegistrationResponse };
'sw/update-registration': { req: SwUpdateRegistrationRequest; res: SwUpdateRegistrationResponse };
'sw/register': { req: SwRegisterRequest; res: SwRegisterResponse };
'sw/unregister': { req: SwUnregisterRequest; res: EmptyResponse };
'test': { req: TestRequest; res: EmptyResponse };
'username/available': { req: UsernameAvailableRequest; res: UsernameAvailableResponse };
'users': { req: UsersRequest; res: UsersResponse };
'users/clips': { req: UsersClipsRequest; res: UsersClipsResponse };
'users/followers': { req: UsersFollowersRequest; res: UsersFollowersResponse };
'users/following': { req: UsersFollowingRequest; res: UsersFollowingResponse };
'users/gallery/posts': { req: UsersGalleryPostsRequest; res: UsersGalleryPostsResponse };
'users/get-frequently-replied-users': { req: UsersGetFrequentlyRepliedUsersRequest; res: UsersGetFrequentlyRepliedUsersResponse };
'users/featured-notes': { req: UsersFeaturedNotesRequest; res: UsersFeaturedNotesResponse };
'users/groups/create': { req: UsersGroupsCreateRequest; res: UsersGroupsCreateResponse };
'users/groups/delete': { req: UsersGroupsDeleteRequest; res: EmptyResponse };
'users/groups/invitations/accept': { req: UsersGroupsInvitationsAcceptRequest; res: EmptyResponse };
'users/groups/invitations/reject': { req: UsersGroupsInvitationsRejectRequest; res: EmptyResponse };
'users/groups/invite': { req: UsersGroupsInviteRequest; res: EmptyResponse };
'users/groups/joined': { req: EmptyRequest; res: UsersGroupsJoinedResponse };
'users/groups/leave': { req: UsersGroupsLeaveRequest; res: EmptyResponse };
'users/groups/owned': { req: EmptyRequest; res: UsersGroupsOwnedResponse };
'users/groups/pull': { req: UsersGroupsPullRequest; res: EmptyResponse };
'users/groups/show': { req: UsersGroupsShowRequest; res: UsersGroupsShowResponse };
'users/groups/transfer': { req: UsersGroupsTransferRequest; res: UsersGroupsTransferResponse };
'users/groups/update': { req: UsersGroupsUpdateRequest; res: UsersGroupsUpdateResponse };
'users/lists/create': { req: UsersListsCreateRequest; res: UsersListsCreateResponse };
'users/lists/delete': { req: UsersListsDeleteRequest; res: EmptyResponse };
'users/lists/list': { req: UsersListsListRequest; res: UsersListsListResponse };
'users/lists/pull': { req: UsersListsPullRequest; res: EmptyResponse };
'users/lists/push': { req: UsersListsPushRequest; res: EmptyResponse };
'users/lists/show': { req: UsersListsShowRequest; res: UsersListsShowResponse };
'users/lists/favorite': { req: UsersListsFavoriteRequest; res: EmptyResponse };
'users/lists/unfavorite': { req: UsersListsUnfavoriteRequest; res: EmptyResponse };
'users/lists/update': { req: UsersListsUpdateRequest; res: UsersListsUpdateResponse };
'users/lists/create-from-public': { req: UsersListsCreateFromPublicRequest; res: UsersListsCreateFromPublicResponse };
'users/lists/update-membership': { req: UsersListsUpdateMembershipRequest; res: EmptyResponse };
'users/lists/get-memberships': { req: UsersListsGetMembershipsRequest; res: EmptyResponse };
'users/notes': { req: UsersNotesRequest; res: UsersNotesResponse };
'users/pages': { req: UsersPagesRequest; res: UsersPagesResponse };
'users/flashs': { req: UsersFlashsRequest; res: UsersFlashsResponse };
'users/reactions': { req: UsersReactionsRequest; res: UsersReactionsResponse };
'users/recommendation': { req: UsersRecommendationRequest; res: UsersRecommendationResponse };
'users/relation': { req: UsersRelationRequest; res: UsersRelationResponse };
'users/report-abuse': { req: UsersReportAbuseRequest; res: EmptyResponse };
'users/search-by-username-and-host': { req: UsersSearchByUsernameAndHostRequest; res: UsersSearchByUsernameAndHostResponse };
'users/search': { req: UsersSearchRequest; res: UsersSearchResponse };
'users/show': { req: UsersShowRequest; res: UsersShowResponse };
'users/stats': { req: UsersStatsRequest; res: UsersStatsResponse };
'users/achievements': { req: UsersAchievementsRequest; res: EmptyResponse };
'users/update-memo': { req: UsersUpdateMemoRequest; res: EmptyResponse };
'users/translate': { req: UsersTranslateRequest; res: UsersTranslateResponse };
'fetch-rss': { req: FetchRssRequest; res: EmptyResponse };
'fetch-external-resources': { req: FetchExternalResourcesRequest; res: EmptyResponse };
'retention': { req: EmptyRequest; res: RetentionResponse };
}

View file

@ -0,0 +1,520 @@
/*
* version: 4.6.0-beta.1
* generatedAt: 2023-12-05T08:05:02.073Z
*/
import { operations } from './types.js';
export type EmptyRequest = Record<string, unknown> | undefined;
export type EmptyResponse = Record<string, unknown> | undefined;
export type AdminMetaResponse = operations['admin/meta']['responses']['200']['content']['application/json'];
export type AdminAbuseReportResolverCreateRequest = operations['admin/abuse-report-resolver/create']['requestBody']['content']['application/json'];
export type AdminAbuseReportResolverCreateResponse = operations['admin/abuse-report-resolver/create']['responses']['200']['content']['application/json'];
export type AdminAbuseReportResolverListRequest = operations['admin/abuse-report-resolver/list']['requestBody']['content']['application/json'];
export type AdminAbuseReportResolverListResponse = operations['admin/abuse-report-resolver/list']['responses']['200']['content']['application/json'];
export type AdminAbuseReportResolverDeleteRequest = operations['admin/abuse-report-resolver/delete']['requestBody']['content']['application/json'];
export type AdminAbuseReportResolverUpdateRequest = operations['admin/abuse-report-resolver/update']['requestBody']['content']['application/json'];
export type AdminAbuseUserReportsRequest = operations['admin/abuse-user-reports']['requestBody']['content']['application/json'];
export type AdminAbuseUserReportsResponse = operations['admin/abuse-user-reports']['responses']['200']['content']['application/json'];
export type AdminAccountsCreateRequest = operations['admin/accounts/create']['requestBody']['content']['application/json'];
export type AdminAccountsCreateResponse = operations['admin/accounts/create']['responses']['200']['content']['application/json'];
export type AdminAccountsDeleteRequest = operations['admin/accounts/delete']['requestBody']['content']['application/json'];
export type AdminAccountsFindByEmailRequest = operations['admin/accounts/find-by-email']['requestBody']['content']['application/json'];
export type AdminAdCreateRequest = operations['admin/ad/create']['requestBody']['content']['application/json'];
export type AdminAdDeleteRequest = operations['admin/ad/delete']['requestBody']['content']['application/json'];
export type AdminAdListRequest = operations['admin/ad/list']['requestBody']['content']['application/json'];
export type AdminAdUpdateRequest = operations['admin/ad/update']['requestBody']['content']['application/json'];
export type AdminAnnouncementsCreateRequest = operations['admin/announcements/create']['requestBody']['content']['application/json'];
export type AdminAnnouncementsCreateResponse = operations['admin/announcements/create']['responses']['200']['content']['application/json'];
export type AdminAnnouncementsDeleteRequest = operations['admin/announcements/delete']['requestBody']['content']['application/json'];
export type AdminAnnouncementsListRequest = operations['admin/announcements/list']['requestBody']['content']['application/json'];
export type AdminAnnouncementsListResponse = operations['admin/announcements/list']['responses']['200']['content']['application/json'];
export type AdminAnnouncementsUpdateRequest = operations['admin/announcements/update']['requestBody']['content']['application/json'];
export type AdminAvatarDecorationsCreateRequest = operations['admin/avatar-decorations/create']['requestBody']['content']['application/json'];
export type AdminAvatarDecorationsDeleteRequest = operations['admin/avatar-decorations/delete']['requestBody']['content']['application/json'];
export type AdminAvatarDecorationsListRequest = operations['admin/avatar-decorations/list']['requestBody']['content']['application/json'];
export type AdminAvatarDecorationsListResponse = operations['admin/avatar-decorations/list']['responses']['200']['content']['application/json'];
export type AdminAvatarDecorationsUpdateRequest = operations['admin/avatar-decorations/update']['requestBody']['content']['application/json'];
export type AdminDeleteAllFilesOfAUserRequest = operations['admin/delete-all-files-of-a-user']['requestBody']['content']['application/json'];
export type AdminUnsetUserAvatarRequest = operations['admin/unset-user-avatar']['requestBody']['content']['application/json'];
export type AdminUnsetUserBannerRequest = operations['admin/unset-user-banner']['requestBody']['content']['application/json'];
export type AdminDriveFilesRequest = operations['admin/drive/files']['requestBody']['content']['application/json'];
export type AdminDriveFilesResponse = operations['admin/drive/files']['responses']['200']['content']['application/json'];
export type AdminDriveShowFileRequest = operations['admin/drive/show-file']['requestBody']['content']['application/json'];
export type AdminDriveShowFileResponse = operations['admin/drive/show-file']['responses']['200']['content']['application/json'];
export type AdminEmojiAddAliasesBulkRequest = operations['admin/emoji/add-aliases-bulk']['requestBody']['content']['application/json'];
export type AdminEmojiAddRequest = operations['admin/emoji/add']['requestBody']['content']['application/json'];
export type AdminEmojiAddsRequest = operations['admin/emoji/adds']['requestBody']['content']['application/json'];
export type AdminEmojiCopyRequest = operations['admin/emoji/copy']['requestBody']['content']['application/json'];
export type AdminEmojiCopyResponse = operations['admin/emoji/copy']['responses']['200']['content']['application/json'];
export type AdminEmojiDeleteBulkRequest = operations['admin/emoji/delete-bulk']['requestBody']['content']['application/json'];
export type AdminEmojiDeleteRequest = operations['admin/emoji/delete']['requestBody']['content']['application/json'];
export type AdminEmojiListRemoteRequest = operations['admin/emoji/list-remote']['requestBody']['content']['application/json'];
export type AdminEmojiListRemoteResponse = operations['admin/emoji/list-remote']['responses']['200']['content']['application/json'];
export type AdminEmojiListRequest = operations['admin/emoji/list']['requestBody']['content']['application/json'];
export type AdminEmojiListResponse = operations['admin/emoji/list']['responses']['200']['content']['application/json'];
export type AdminEmojiRemoveAliasesBulkRequest = operations['admin/emoji/remove-aliases-bulk']['requestBody']['content']['application/json'];
export type AdminEmojiSetAliasesBulkRequest = operations['admin/emoji/set-aliases-bulk']['requestBody']['content']['application/json'];
export type AdminEmojiSetCategoryBulkRequest = operations['admin/emoji/set-category-bulk']['requestBody']['content']['application/json'];
export type AdminEmojiSetLicenseBulkRequest = operations['admin/emoji/set-license-bulk']['requestBody']['content']['application/json'];
export type AdminEmojiStealRequest = operations['admin/emoji/steal']['requestBody']['content']['application/json'];
export type AdminEmojiStealResponse = operations['admin/emoji/steal']['responses']['200']['content']['application/json'];
export type AdminEmojiUpdateRequest = operations['admin/emoji/update']['requestBody']['content']['application/json'];
export type AdminFederationDeleteAllFilesRequest = operations['admin/federation/delete-all-files']['requestBody']['content']['application/json'];
export type AdminFederationRefreshRemoteInstanceMetadataRequest = operations['admin/federation/refresh-remote-instance-metadata']['requestBody']['content']['application/json'];
export type AdminFederationRemoveAllFollowingRequest = operations['admin/federation/remove-all-following']['requestBody']['content']['application/json'];
export type AdminFederationUpdateInstanceRequest = operations['admin/federation/update-instance']['requestBody']['content']['application/json'];
export type AdminGetTableStatsResponse = operations['admin/get-table-stats']['responses']['200']['content']['application/json'];
export type AdminGetUserIpsRequest = operations['admin/get-user-ips']['requestBody']['content']['application/json'];
export type AdminInviteCreateRequest = operations['admin/invite/create']['requestBody']['content']['application/json'];
export type AdminInviteCreateResponse = operations['admin/invite/create']['responses']['200']['content']['application/json'];
export type AdminInviteListRequest = operations['admin/invite/list']['requestBody']['content']['application/json'];
export type AdminInviteListResponse = operations['admin/invite/list']['responses']['200']['content']['application/json'];
export type AdminPromoCreateRequest = operations['admin/promo/create']['requestBody']['content']['application/json'];
export type AdminQueueDeliverDelayedResponse = operations['admin/queue/deliver-delayed']['responses']['200']['content']['application/json'];
export type AdminQueueInboxDelayedResponse = operations['admin/queue/inbox-delayed']['responses']['200']['content']['application/json'];
export type AdminQueuePromoteRequest = operations['admin/queue/promote']['requestBody']['content']['application/json'];
export type AdminQueueStatsResponse = operations['admin/queue/stats']['responses']['200']['content']['application/json'];
export type AdminRelaysAddRequest = operations['admin/relays/add']['requestBody']['content']['application/json'];
export type AdminRelaysAddResponse = operations['admin/relays/add']['responses']['200']['content']['application/json'];
export type AdminRelaysListResponse = operations['admin/relays/list']['responses']['200']['content']['application/json'];
export type AdminRelaysRemoveRequest = operations['admin/relays/remove']['requestBody']['content']['application/json'];
export type AdminResetPasswordRequest = operations['admin/reset-password']['requestBody']['content']['application/json'];
export type AdminResetPasswordResponse = operations['admin/reset-password']['responses']['200']['content']['application/json'];
export type AdminResolveAbuseUserReportRequest = operations['admin/resolve-abuse-user-report']['requestBody']['content']['application/json'];
export type AdminSendEmailRequest = operations['admin/send-email']['requestBody']['content']['application/json'];
export type AdminServerInfoResponse = operations['admin/server-info']['responses']['200']['content']['application/json'];
export type AdminShowModerationLogsRequest = operations['admin/show-moderation-logs']['requestBody']['content']['application/json'];
export type AdminShowModerationLogsResponse = operations['admin/show-moderation-logs']['responses']['200']['content']['application/json'];
export type AdminShowUserRequest = operations['admin/show-user']['requestBody']['content']['application/json'];
export type AdminShowUserResponse = operations['admin/show-user']['responses']['200']['content']['application/json'];
export type AdminShowUsersRequest = operations['admin/show-users']['requestBody']['content']['application/json'];
export type AdminShowUsersResponse = operations['admin/show-users']['responses']['200']['content']['application/json'];
export type AdminSuspendUserRequest = operations['admin/suspend-user']['requestBody']['content']['application/json'];
export type AdminUnsuspendUserRequest = operations['admin/unsuspend-user']['requestBody']['content']['application/json'];
export type AdminUpdateMetaRequest = operations['admin/update-meta']['requestBody']['content']['application/json'];
export type AdminDeleteAccountRequest = operations['admin/delete-account']['requestBody']['content']['application/json'];
export type AdminDeleteAccountResponse = operations['admin/delete-account']['responses']['200']['content']['application/json'];
export type AdminUpdateUserNoteRequest = operations['admin/update-user-note']['requestBody']['content']['application/json'];
export type AdminRolesCreateRequest = operations['admin/roles/create']['requestBody']['content']['application/json'];
export type AdminRolesDeleteRequest = operations['admin/roles/delete']['requestBody']['content']['application/json'];
export type AdminRolesShowRequest = operations['admin/roles/show']['requestBody']['content']['application/json'];
export type AdminRolesUpdateRequest = operations['admin/roles/update']['requestBody']['content']['application/json'];
export type AdminRolesAssignRequest = operations['admin/roles/assign']['requestBody']['content']['application/json'];
export type AdminRolesUnassignRequest = operations['admin/roles/unassign']['requestBody']['content']['application/json'];
export type AdminRolesUpdateDefaultPoliciesRequest = operations['admin/roles/update-default-policies']['requestBody']['content']['application/json'];
export type AdminRolesUsersRequest = operations['admin/roles/users']['requestBody']['content']['application/json'];
export type AnnouncementsRequest = operations['announcements']['requestBody']['content']['application/json'];
export type AnnouncementsResponse = operations['announcements']['responses']['200']['content']['application/json'];
export type AntennasCreateRequest = operations['antennas/create']['requestBody']['content']['application/json'];
export type AntennasCreateResponse = operations['antennas/create']['responses']['200']['content']['application/json'];
export type AntennasDeleteRequest = operations['antennas/delete']['requestBody']['content']['application/json'];
export type AntennasListResponse = operations['antennas/list']['responses']['200']['content']['application/json'];
export type AntennasNotesRequest = operations['antennas/notes']['requestBody']['content']['application/json'];
export type AntennasNotesResponse = operations['antennas/notes']['responses']['200']['content']['application/json'];
export type AntennasShowRequest = operations['antennas/show']['requestBody']['content']['application/json'];
export type AntennasShowResponse = operations['antennas/show']['responses']['200']['content']['application/json'];
export type AntennasUpdateRequest = operations['antennas/update']['requestBody']['content']['application/json'];
export type AntennasUpdateResponse = operations['antennas/update']['responses']['200']['content']['application/json'];
export type ApGetRequest = operations['ap/get']['requestBody']['content']['application/json'];
export type ApGetResponse = operations['ap/get']['responses']['200']['content']['application/json'];
export type ApShowRequest = operations['ap/show']['requestBody']['content']['application/json'];
export type ApShowResponse = operations['ap/show']['responses']['200']['content']['application/json'];
export type AppCreateRequest = operations['app/create']['requestBody']['content']['application/json'];
export type AppCreateResponse = operations['app/create']['responses']['200']['content']['application/json'];
export type AppShowRequest = operations['app/show']['requestBody']['content']['application/json'];
export type AppShowResponse = operations['app/show']['responses']['200']['content']['application/json'];
export type AuthSessionGenerateRequest = operations['auth/session/generate']['requestBody']['content']['application/json'];
export type AuthSessionGenerateResponse = operations['auth/session/generate']['responses']['200']['content']['application/json'];
export type AuthSessionShowRequest = operations['auth/session/show']['requestBody']['content']['application/json'];
export type AuthSessionShowResponse = operations['auth/session/show']['responses']['200']['content']['application/json'];
export type AuthSessionUserkeyRequest = operations['auth/session/userkey']['requestBody']['content']['application/json'];
export type AuthSessionUserkeyResponse = operations['auth/session/userkey']['responses']['200']['content']['application/json'];
export type BlockingCreateRequest = operations['blocking/create']['requestBody']['content']['application/json'];
export type BlockingCreateResponse = operations['blocking/create']['responses']['200']['content']['application/json'];
export type BlockingDeleteRequest = operations['blocking/delete']['requestBody']['content']['application/json'];
export type BlockingDeleteResponse = operations['blocking/delete']['responses']['200']['content']['application/json'];
export type BlockingListRequest = operations['blocking/list']['requestBody']['content']['application/json'];
export type BlockingListResponse = operations['blocking/list']['responses']['200']['content']['application/json'];
export type ChannelsCreateRequest = operations['channels/create']['requestBody']['content']['application/json'];
export type ChannelsCreateResponse = operations['channels/create']['responses']['200']['content']['application/json'];
export type ChannelsFeaturedResponse = operations['channels/featured']['responses']['200']['content']['application/json'];
export type ChannelsFollowRequest = operations['channels/follow']['requestBody']['content']['application/json'];
export type ChannelsFollowedRequest = operations['channels/followed']['requestBody']['content']['application/json'];
export type ChannelsFollowedResponse = operations['channels/followed']['responses']['200']['content']['application/json'];
export type ChannelsOwnedRequest = operations['channels/owned']['requestBody']['content']['application/json'];
export type ChannelsOwnedResponse = operations['channels/owned']['responses']['200']['content']['application/json'];
export type ChannelsShowRequest = operations['channels/show']['requestBody']['content']['application/json'];
export type ChannelsShowResponse = operations['channels/show']['responses']['200']['content']['application/json'];
export type ChannelsTimelineRequest = operations['channels/timeline']['requestBody']['content']['application/json'];
export type ChannelsTimelineResponse = operations['channels/timeline']['responses']['200']['content']['application/json'];
export type ChannelsUnfollowRequest = operations['channels/unfollow']['requestBody']['content']['application/json'];
export type ChannelsUpdateRequest = operations['channels/update']['requestBody']['content']['application/json'];
export type ChannelsUpdateResponse = operations['channels/update']['responses']['200']['content']['application/json'];
export type ChannelsFavoriteRequest = operations['channels/favorite']['requestBody']['content']['application/json'];
export type ChannelsUnfavoriteRequest = operations['channels/unfavorite']['requestBody']['content']['application/json'];
export type ChannelsMyFavoritesResponse = operations['channels/my-favorites']['responses']['200']['content']['application/json'];
export type ChannelsSearchRequest = operations['channels/search']['requestBody']['content']['application/json'];
export type ChannelsSearchResponse = operations['channels/search']['responses']['200']['content']['application/json'];
export type ChartsActiveUsersRequest = operations['charts/active-users']['requestBody']['content']['application/json'];
export type ChartsActiveUsersResponse = operations['charts/active-users']['responses']['200']['content']['application/json'];
export type ChartsApRequestRequest = operations['charts/ap-request']['requestBody']['content']['application/json'];
export type ChartsApRequestResponse = operations['charts/ap-request']['responses']['200']['content']['application/json'];
export type ChartsDriveRequest = operations['charts/drive']['requestBody']['content']['application/json'];
export type ChartsDriveResponse = operations['charts/drive']['responses']['200']['content']['application/json'];
export type ChartsFederationRequest = operations['charts/federation']['requestBody']['content']['application/json'];
export type ChartsFederationResponse = operations['charts/federation']['responses']['200']['content']['application/json'];
export type ChartsInstanceRequest = operations['charts/instance']['requestBody']['content']['application/json'];
export type ChartsInstanceResponse = operations['charts/instance']['responses']['200']['content']['application/json'];
export type ChartsNotesRequest = operations['charts/notes']['requestBody']['content']['application/json'];
export type ChartsNotesResponse = operations['charts/notes']['responses']['200']['content']['application/json'];
export type ChartsUserDriveRequest = operations['charts/user/drive']['requestBody']['content']['application/json'];
export type ChartsUserDriveResponse = operations['charts/user/drive']['responses']['200']['content']['application/json'];
export type ChartsUserFollowingRequest = operations['charts/user/following']['requestBody']['content']['application/json'];
export type ChartsUserFollowingResponse = operations['charts/user/following']['responses']['200']['content']['application/json'];
export type ChartsUserNotesRequest = operations['charts/user/notes']['requestBody']['content']['application/json'];
export type ChartsUserNotesResponse = operations['charts/user/notes']['responses']['200']['content']['application/json'];
export type ChartsUserPvRequest = operations['charts/user/pv']['requestBody']['content']['application/json'];
export type ChartsUserPvResponse = operations['charts/user/pv']['responses']['200']['content']['application/json'];
export type ChartsUserReactionsRequest = operations['charts/user/reactions']['requestBody']['content']['application/json'];
export type ChartsUserReactionsResponse = operations['charts/user/reactions']['responses']['200']['content']['application/json'];
export type ChartsUsersRequest = operations['charts/users']['requestBody']['content']['application/json'];
export type ChartsUsersResponse = operations['charts/users']['responses']['200']['content']['application/json'];
export type ClipsAddNoteRequest = operations['clips/add-note']['requestBody']['content']['application/json'];
export type ClipsRemoveNoteRequest = operations['clips/remove-note']['requestBody']['content']['application/json'];
export type ClipsCreateRequest = operations['clips/create']['requestBody']['content']['application/json'];
export type ClipsCreateResponse = operations['clips/create']['responses']['200']['content']['application/json'];
export type ClipsDeleteRequest = operations['clips/delete']['requestBody']['content']['application/json'];
export type ClipsListResponse = operations['clips/list']['responses']['200']['content']['application/json'];
export type ClipsNotesRequest = operations['clips/notes']['requestBody']['content']['application/json'];
export type ClipsNotesResponse = operations['clips/notes']['responses']['200']['content']['application/json'];
export type ClipsShowRequest = operations['clips/show']['requestBody']['content']['application/json'];
export type ClipsShowResponse = operations['clips/show']['responses']['200']['content']['application/json'];
export type ClipsUpdateRequest = operations['clips/update']['requestBody']['content']['application/json'];
export type ClipsUpdateResponse = operations['clips/update']['responses']['200']['content']['application/json'];
export type ClipsFavoriteRequest = operations['clips/favorite']['requestBody']['content']['application/json'];
export type ClipsUnfavoriteRequest = operations['clips/unfavorite']['requestBody']['content']['application/json'];
export type ClipsMyFavoritesResponse = operations['clips/my-favorites']['responses']['200']['content']['application/json'];
export type DriveResponse = operations['drive']['responses']['200']['content']['application/json'];
export type DriveFilesRequest = operations['drive/files']['requestBody']['content']['application/json'];
export type DriveFilesResponse = operations['drive/files']['responses']['200']['content']['application/json'];
export type DriveFilesAttachedNotesRequest = operations['drive/files/attached-notes']['requestBody']['content']['application/json'];
export type DriveFilesAttachedNotesResponse = operations['drive/files/attached-notes']['responses']['200']['content']['application/json'];
export type DriveFilesCheckExistenceRequest = operations['drive/files/check-existence']['requestBody']['content']['application/json'];
export type DriveFilesCheckExistenceResponse = operations['drive/files/check-existence']['responses']['200']['content']['application/json'];
export type DriveFilesCreateRequest = operations['drive/files/create']['requestBody']['content']['multipart/form-data'];
export type DriveFilesCreateResponse = operations['drive/files/create']['responses']['200']['content']['application/json'];
export type DriveFilesDeleteRequest = operations['drive/files/delete']['requestBody']['content']['application/json'];
export type DriveFilesFindByHashRequest = operations['drive/files/find-by-hash']['requestBody']['content']['application/json'];
export type DriveFilesFindByHashResponse = operations['drive/files/find-by-hash']['responses']['200']['content']['application/json'];
export type DriveFilesFindRequest = operations['drive/files/find']['requestBody']['content']['application/json'];
export type DriveFilesFindResponse = operations['drive/files/find']['responses']['200']['content']['application/json'];
export type DriveFilesShowRequest = operations['drive/files/show']['requestBody']['content']['application/json'];
export type DriveFilesShowResponse = operations['drive/files/show']['responses']['200']['content']['application/json'];
export type DriveFilesUpdateRequest = operations['drive/files/update']['requestBody']['content']['application/json'];
export type DriveFilesUpdateResponse = operations['drive/files/update']['responses']['200']['content']['application/json'];
export type DriveFilesUploadFromUrlRequest = operations['drive/files/upload-from-url']['requestBody']['content']['application/json'];
export type DriveFoldersRequest = operations['drive/folders']['requestBody']['content']['application/json'];
export type DriveFoldersResponse = operations['drive/folders']['responses']['200']['content']['application/json'];
export type DriveFoldersCreateRequest = operations['drive/folders/create']['requestBody']['content']['application/json'];
export type DriveFoldersCreateResponse = operations['drive/folders/create']['responses']['200']['content']['application/json'];
export type DriveFoldersDeleteRequest = operations['drive/folders/delete']['requestBody']['content']['application/json'];
export type DriveFoldersFindRequest = operations['drive/folders/find']['requestBody']['content']['application/json'];
export type DriveFoldersFindResponse = operations['drive/folders/find']['responses']['200']['content']['application/json'];
export type DriveFoldersShowRequest = operations['drive/folders/show']['requestBody']['content']['application/json'];
export type DriveFoldersShowResponse = operations['drive/folders/show']['responses']['200']['content']['application/json'];
export type DriveFoldersUpdateRequest = operations['drive/folders/update']['requestBody']['content']['application/json'];
export type DriveFoldersUpdateResponse = operations['drive/folders/update']['responses']['200']['content']['application/json'];
export type DriveStreamRequest = operations['drive/stream']['requestBody']['content']['application/json'];
export type DriveStreamResponse = operations['drive/stream']['responses']['200']['content']['application/json'];
export type EmailAddressAvailableRequest = operations['email-address/available']['requestBody']['content']['application/json'];
export type EmailAddressAvailableResponse = operations['email-address/available']['responses']['200']['content']['application/json'];
export type EndpointRequest = operations['endpoint']['requestBody']['content']['application/json'];
export type EndpointsResponse = operations['endpoints']['responses']['200']['content']['application/json'];
export type FederationFollowersRequest = operations['federation/followers']['requestBody']['content']['application/json'];
export type FederationFollowersResponse = operations['federation/followers']['responses']['200']['content']['application/json'];
export type FederationFollowingRequest = operations['federation/following']['requestBody']['content']['application/json'];
export type FederationFollowingResponse = operations['federation/following']['responses']['200']['content']['application/json'];
export type FederationInstancesRequest = operations['federation/instances']['requestBody']['content']['application/json'];
export type FederationInstancesResponse = operations['federation/instances']['responses']['200']['content']['application/json'];
export type FederationShowInstanceRequest = operations['federation/show-instance']['requestBody']['content']['application/json'];
export type FederationShowInstanceResponse = operations['federation/show-instance']['responses']['200']['content']['application/json'];
export type FederationUpdateRemoteUserRequest = operations['federation/update-remote-user']['requestBody']['content']['application/json'];
export type FederationUsersRequest = operations['federation/users']['requestBody']['content']['application/json'];
export type FederationUsersResponse = operations['federation/users']['responses']['200']['content']['application/json'];
export type FederationStatsRequest = operations['federation/stats']['requestBody']['content']['application/json'];
export type FollowingCreateRequest = operations['following/create']['requestBody']['content']['application/json'];
export type FollowingCreateResponse = operations['following/create']['responses']['200']['content']['application/json'];
export type FollowingDeleteRequest = operations['following/delete']['requestBody']['content']['application/json'];
export type FollowingDeleteResponse = operations['following/delete']['responses']['200']['content']['application/json'];
export type FollowingUpdateRequest = operations['following/update']['requestBody']['content']['application/json'];
export type FollowingUpdateResponse = operations['following/update']['responses']['200']['content']['application/json'];
export type FollowingUpdateAllRequest = operations['following/update-all']['requestBody']['content']['application/json'];
export type FollowingInvalidateRequest = operations['following/invalidate']['requestBody']['content']['application/json'];
export type FollowingInvalidateResponse = operations['following/invalidate']['responses']['200']['content']['application/json'];
export type FollowingRequestsAcceptRequest = operations['following/requests/accept']['requestBody']['content']['application/json'];
export type FollowingRequestsCancelRequest = operations['following/requests/cancel']['requestBody']['content']['application/json'];
export type FollowingRequestsCancelResponse = operations['following/requests/cancel']['responses']['200']['content']['application/json'];
export type FollowingRequestsListRequest = operations['following/requests/list']['requestBody']['content']['application/json'];
export type FollowingRequestsListResponse = operations['following/requests/list']['responses']['200']['content']['application/json'];
export type FollowingRequestsRejectRequest = operations['following/requests/reject']['requestBody']['content']['application/json'];
export type GalleryFeaturedRequest = operations['gallery/featured']['requestBody']['content']['application/json'];
export type GalleryFeaturedResponse = operations['gallery/featured']['responses']['200']['content']['application/json'];
export type GalleryPopularResponse = operations['gallery/popular']['responses']['200']['content']['application/json'];
export type GalleryPostsRequest = operations['gallery/posts']['requestBody']['content']['application/json'];
export type GalleryPostsResponse = operations['gallery/posts']['responses']['200']['content']['application/json'];
export type GalleryPostsCreateRequest = operations['gallery/posts/create']['requestBody']['content']['application/json'];
export type GalleryPostsCreateResponse = operations['gallery/posts/create']['responses']['200']['content']['application/json'];
export type GalleryPostsDeleteRequest = operations['gallery/posts/delete']['requestBody']['content']['application/json'];
export type GalleryPostsLikeRequest = operations['gallery/posts/like']['requestBody']['content']['application/json'];
export type GalleryPostsShowRequest = operations['gallery/posts/show']['requestBody']['content']['application/json'];
export type GalleryPostsShowResponse = operations['gallery/posts/show']['responses']['200']['content']['application/json'];
export type GalleryPostsUnlikeRequest = operations['gallery/posts/unlike']['requestBody']['content']['application/json'];
export type GalleryPostsUpdateRequest = operations['gallery/posts/update']['requestBody']['content']['application/json'];
export type GalleryPostsUpdateResponse = operations['gallery/posts/update']['responses']['200']['content']['application/json'];
export type GetAvatarDecorationsResponse = operations['get-avatar-decorations']['responses']['200']['content']['application/json'];
export type HashtagsListRequest = operations['hashtags/list']['requestBody']['content']['application/json'];
export type HashtagsListResponse = operations['hashtags/list']['responses']['200']['content']['application/json'];
export type HashtagsSearchRequest = operations['hashtags/search']['requestBody']['content']['application/json'];
export type HashtagsSearchResponse = operations['hashtags/search']['responses']['200']['content']['application/json'];
export type HashtagsShowRequest = operations['hashtags/show']['requestBody']['content']['application/json'];
export type HashtagsShowResponse = operations['hashtags/show']['responses']['200']['content']['application/json'];
export type HashtagsTrendResponse = operations['hashtags/trend']['responses']['200']['content']['application/json'];
export type HashtagsUsersRequest = operations['hashtags/users']['requestBody']['content']['application/json'];
export type HashtagsUsersResponse = operations['hashtags/users']['responses']['200']['content']['application/json'];
export type IResponse = operations['i']['responses']['200']['content']['application/json'];
export type IClaimAchievementRequest = operations['i/claim-achievement']['requestBody']['content']['application/json'];
export type IFavoritesRequest = operations['i/favorites']['requestBody']['content']['application/json'];
export type IFavoritesResponse = operations['i/favorites']['responses']['200']['content']['application/json'];
export type IGalleryLikesRequest = operations['i/gallery/likes']['requestBody']['content']['application/json'];
export type IGalleryLikesResponse = operations['i/gallery/likes']['responses']['200']['content']['application/json'];
export type IGalleryPostsRequest = operations['i/gallery/posts']['requestBody']['content']['application/json'];
export type IGalleryPostsResponse = operations['i/gallery/posts']['responses']['200']['content']['application/json'];
export type INotificationsRequest = operations['i/notifications']['requestBody']['content']['application/json'];
export type INotificationsResponse = operations['i/notifications']['responses']['200']['content']['application/json'];
export type INotificationsGroupedRequest = operations['i/notifications-grouped']['requestBody']['content']['application/json'];
export type INotificationsGroupedResponse = operations['i/notifications-grouped']['responses']['200']['content']['application/json'];
export type IPageLikesRequest = operations['i/page-likes']['requestBody']['content']['application/json'];
export type IPageLikesResponse = operations['i/page-likes']['responses']['200']['content']['application/json'];
export type IPagesRequest = operations['i/pages']['requestBody']['content']['application/json'];
export type IPagesResponse = operations['i/pages']['responses']['200']['content']['application/json'];
export type IPinRequest = operations['i/pin']['requestBody']['content']['application/json'];
export type IPinResponse = operations['i/pin']['responses']['200']['content']['application/json'];
export type IReadAnnouncementRequest = operations['i/read-announcement']['requestBody']['content']['application/json'];
export type IRegistryGetAllRequest = operations['i/registry/get-all']['requestBody']['content']['application/json'];
export type IRegistryGetDetailRequest = operations['i/registry/get-detail']['requestBody']['content']['application/json'];
export type IRegistryGetRequest = operations['i/registry/get']['requestBody']['content']['application/json'];
export type IRegistryKeysWithTypeRequest = operations['i/registry/keys-with-type']['requestBody']['content']['application/json'];
export type IRegistryKeysRequest = operations['i/registry/keys']['requestBody']['content']['application/json'];
export type IRegistryRemoveRequest = operations['i/registry/remove']['requestBody']['content']['application/json'];
export type IRegistrySetRequest = operations['i/registry/set']['requestBody']['content']['application/json'];
export type IUnpinRequest = operations['i/unpin']['requestBody']['content']['application/json'];
export type IUnpinResponse = operations['i/unpin']['responses']['200']['content']['application/json'];
export type IUpdateRequest = operations['i/update']['requestBody']['content']['application/json'];
export type IUpdateResponse = operations['i/update']['responses']['200']['content']['application/json'];
export type IUserGroupInvitesRequest = operations['i/user-group-invites']['requestBody']['content']['application/json'];
export type IUserGroupInvitesResponse = operations['i/user-group-invites']['responses']['200']['content']['application/json'];
export type IWebhooksCreateRequest = operations['i/webhooks/create']['requestBody']['content']['application/json'];
export type IWebhooksShowRequest = operations['i/webhooks/show']['requestBody']['content']['application/json'];
export type IWebhooksUpdateRequest = operations['i/webhooks/update']['requestBody']['content']['application/json'];
export type IWebhooksDeleteRequest = operations['i/webhooks/delete']['requestBody']['content']['application/json'];
export type InviteCreateResponse = operations['invite/create']['responses']['200']['content']['application/json'];
export type InviteDeleteRequest = operations['invite/delete']['requestBody']['content']['application/json'];
export type InviteListRequest = operations['invite/list']['requestBody']['content']['application/json'];
export type InviteListResponse = operations['invite/list']['responses']['200']['content']['application/json'];
export type InviteLimitResponse = operations['invite/limit']['responses']['200']['content']['application/json'];
export type MessagingHistoryRequest = operations['messaging/history']['requestBody']['content']['application/json'];
export type MessagingHistoryResponse = operations['messaging/history']['responses']['200']['content']['application/json'];
export type MessagingMessagesRequest = operations['messaging/messages']['requestBody']['content']['application/json'];
export type MessagingMessagesResponse = operations['messaging/messages']['responses']['200']['content']['application/json'];
export type MessagingMessagesCreateRequest = operations['messaging/messages/create']['requestBody']['content']['application/json'];
export type MessagingMessagesCreateResponse = operations['messaging/messages/create']['responses']['200']['content']['application/json'];
export type MessagingMessagesDeleteRequest = operations['messaging/messages/delete']['requestBody']['content']['application/json'];
export type MessagingMessagesReadRequest = operations['messaging/messages/read']['requestBody']['content']['application/json'];
export type MetaRequest = operations['meta']['requestBody']['content']['application/json'];
export type MetaResponse = operations['meta']['responses']['200']['content']['application/json'];
export type EmojisResponse = operations['emojis']['responses']['200']['content']['application/json'];
export type EmojiRequest = operations['emoji']['requestBody']['content']['application/json'];
export type EmojiResponse = operations['emoji']['responses']['200']['content']['application/json'];
export type MuteCreateRequest = operations['mute/create']['requestBody']['content']['application/json'];
export type MuteDeleteRequest = operations['mute/delete']['requestBody']['content']['application/json'];
export type MuteListRequest = operations['mute/list']['requestBody']['content']['application/json'];
export type MuteListResponse = operations['mute/list']['responses']['200']['content']['application/json'];
export type RenoteMuteCreateRequest = operations['renote-mute/create']['requestBody']['content']['application/json'];
export type RenoteMuteDeleteRequest = operations['renote-mute/delete']['requestBody']['content']['application/json'];
export type RenoteMuteListRequest = operations['renote-mute/list']['requestBody']['content']['application/json'];
export type RenoteMuteListResponse = operations['renote-mute/list']['responses']['200']['content']['application/json'];
export type MyAppsRequest = operations['my/apps']['requestBody']['content']['application/json'];
export type MyAppsResponse = operations['my/apps']['responses']['200']['content']['application/json'];
export type NotesRequest = operations['notes']['requestBody']['content']['application/json'];
export type NotesResponse = operations['notes']['responses']['200']['content']['application/json'];
export type NotesChildrenRequest = operations['notes/children']['requestBody']['content']['application/json'];
export type NotesChildrenResponse = operations['notes/children']['responses']['200']['content']['application/json'];
export type NotesClipsRequest = operations['notes/clips']['requestBody']['content']['application/json'];
export type NotesClipsResponse = operations['notes/clips']['responses']['200']['content']['application/json'];
export type NotesConversationRequest = operations['notes/conversation']['requestBody']['content']['application/json'];
export type NotesConversationResponse = operations['notes/conversation']['responses']['200']['content']['application/json'];
export type NotesCreateRequest = operations['notes/create']['requestBody']['content']['application/json'];
export type NotesCreateResponse = operations['notes/create']['responses']['200']['content']['application/json'];
export type NotesDeleteRequest = operations['notes/delete']['requestBody']['content']['application/json'];
export type NotesUpdateRequest = operations['notes/update']['requestBody']['content']['application/json'];
export type NotesFavoritesCreateRequest = operations['notes/favorites/create']['requestBody']['content']['application/json'];
export type NotesFavoritesDeleteRequest = operations['notes/favorites/delete']['requestBody']['content']['application/json'];
export type NotesFeaturedRequest = operations['notes/featured']['requestBody']['content']['application/json'];
export type NotesFeaturedResponse = operations['notes/featured']['responses']['200']['content']['application/json'];
export type NotesGlobalTimelineRequest = operations['notes/global-timeline']['requestBody']['content']['application/json'];
export type NotesGlobalTimelineResponse = operations['notes/global-timeline']['responses']['200']['content']['application/json'];
export type NotesHybridTimelineRequest = operations['notes/hybrid-timeline']['requestBody']['content']['application/json'];
export type NotesHybridTimelineResponse = operations['notes/hybrid-timeline']['responses']['200']['content']['application/json'];
export type NotesLocalTimelineRequest = operations['notes/local-timeline']['requestBody']['content']['application/json'];
export type NotesLocalTimelineResponse = operations['notes/local-timeline']['responses']['200']['content']['application/json'];
export type NotesMentionsRequest = operations['notes/mentions']['requestBody']['content']['application/json'];
export type NotesMentionsResponse = operations['notes/mentions']['responses']['200']['content']['application/json'];
export type NotesPollsRecommendationRequest = operations['notes/polls/recommendation']['requestBody']['content']['application/json'];
export type NotesPollsRecommendationResponse = operations['notes/polls/recommendation']['responses']['200']['content']['application/json'];
export type NotesPollsVoteRequest = operations['notes/polls/vote']['requestBody']['content']['application/json'];
export type NotesEventsSearchRequest = operations['notes/events/search']['requestBody']['content']['application/json'];
export type NotesEventsSearchResponse = operations['notes/events/search']['responses']['200']['content']['application/json'];
export type NotesReactionsRequest = operations['notes/reactions']['requestBody']['content']['application/json'];
export type NotesReactionsResponse = operations['notes/reactions']['responses']['200']['content']['application/json'];
export type NotesReactionsCreateRequest = operations['notes/reactions/create']['requestBody']['content']['application/json'];
export type NotesReactionsDeleteRequest = operations['notes/reactions/delete']['requestBody']['content']['application/json'];
export type NotesRenotesRequest = operations['notes/renotes']['requestBody']['content']['application/json'];
export type NotesRenotesResponse = operations['notes/renotes']['responses']['200']['content']['application/json'];
export type NotesRepliesRequest = operations['notes/replies']['requestBody']['content']['application/json'];
export type NotesRepliesResponse = operations['notes/replies']['responses']['200']['content']['application/json'];
export type NotesSearchByTagRequest = operations['notes/search-by-tag']['requestBody']['content']['application/json'];
export type NotesSearchByTagResponse = operations['notes/search-by-tag']['responses']['200']['content']['application/json'];
export type NotesSearchRequest = operations['notes/search']['requestBody']['content']['application/json'];
export type NotesSearchResponse = operations['notes/search']['responses']['200']['content']['application/json'];
export type NotesShowRequest = operations['notes/show']['requestBody']['content']['application/json'];
export type NotesShowResponse = operations['notes/show']['responses']['200']['content']['application/json'];
export type NotesStateRequest = operations['notes/state']['requestBody']['content']['application/json'];
export type NotesStateResponse = operations['notes/state']['responses']['200']['content']['application/json'];
export type NotesThreadMutingCreateRequest = operations['notes/thread-muting/create']['requestBody']['content']['application/json'];
export type NotesThreadMutingDeleteRequest = operations['notes/thread-muting/delete']['requestBody']['content']['application/json'];
export type NotesTimelineRequest = operations['notes/timeline']['requestBody']['content']['application/json'];
export type NotesTimelineResponse = operations['notes/timeline']['responses']['200']['content']['application/json'];
export type NotesTranslateRequest = operations['notes/translate']['requestBody']['content']['application/json'];
export type NotesTranslateResponse = operations['notes/translate']['responses']['200']['content']['application/json'];
export type NotesUnrenoteRequest = operations['notes/unrenote']['requestBody']['content']['application/json'];
export type NotesUserListTimelineRequest = operations['notes/user-list-timeline']['requestBody']['content']['application/json'];
export type NotesUserListTimelineResponse = operations['notes/user-list-timeline']['responses']['200']['content']['application/json'];
export type NotificationsCreateRequest = operations['notifications/create']['requestBody']['content']['application/json'];
export type PagesCreateRequest = operations['pages/create']['requestBody']['content']['application/json'];
export type PagesCreateResponse = operations['pages/create']['responses']['200']['content']['application/json'];
export type PagesDeleteRequest = operations['pages/delete']['requestBody']['content']['application/json'];
export type PagesFeaturedResponse = operations['pages/featured']['responses']['200']['content']['application/json'];
export type PagesLikeRequest = operations['pages/like']['requestBody']['content']['application/json'];
export type PagesShowRequest = operations['pages/show']['requestBody']['content']['application/json'];
export type PagesShowResponse = operations['pages/show']['responses']['200']['content']['application/json'];
export type PagesUnlikeRequest = operations['pages/unlike']['requestBody']['content']['application/json'];
export type PagesUpdateRequest = operations['pages/update']['requestBody']['content']['application/json'];
export type FlashCreateRequest = operations['flash/create']['requestBody']['content']['application/json'];
export type FlashDeleteRequest = operations['flash/delete']['requestBody']['content']['application/json'];
export type FlashFeaturedResponse = operations['flash/featured']['responses']['200']['content']['application/json'];
export type FlashLikeRequest = operations['flash/like']['requestBody']['content']['application/json'];
export type FlashShowRequest = operations['flash/show']['requestBody']['content']['application/json'];
export type FlashShowResponse = operations['flash/show']['responses']['200']['content']['application/json'];
export type FlashUnlikeRequest = operations['flash/unlike']['requestBody']['content']['application/json'];
export type FlashUpdateRequest = operations['flash/update']['requestBody']['content']['application/json'];
export type FlashMyRequest = operations['flash/my']['requestBody']['content']['application/json'];
export type FlashMyResponse = operations['flash/my']['responses']['200']['content']['application/json'];
export type FlashMyLikesRequest = operations['flash/my-likes']['requestBody']['content']['application/json'];
export type FlashMyLikesResponse = operations['flash/my-likes']['responses']['200']['content']['application/json'];
export type PingResponse = operations['ping']['responses']['200']['content']['application/json'];
export type PinnedUsersResponse = operations['pinned-users']['responses']['200']['content']['application/json'];
export type PromoReadRequest = operations['promo/read']['requestBody']['content']['application/json'];
export type RolesShowRequest = operations['roles/show']['requestBody']['content']['application/json'];
export type RolesUsersRequest = operations['roles/users']['requestBody']['content']['application/json'];
export type RolesNotesRequest = operations['roles/notes']['requestBody']['content']['application/json'];
export type RolesNotesResponse = operations['roles/notes']['responses']['200']['content']['application/json'];
export type RequestResetPasswordRequest = operations['request-reset-password']['requestBody']['content']['application/json'];
export type ResetPasswordRequest = operations['reset-password']['requestBody']['content']['application/json'];
export type StatsResponse = operations['stats']['responses']['200']['content']['application/json'];
export type SwShowRegistrationRequest = operations['sw/show-registration']['requestBody']['content']['application/json'];
export type SwShowRegistrationResponse = operations['sw/show-registration']['responses']['200']['content']['application/json'];
export type SwUpdateRegistrationRequest = operations['sw/update-registration']['requestBody']['content']['application/json'];
export type SwUpdateRegistrationResponse = operations['sw/update-registration']['responses']['200']['content']['application/json'];
export type SwRegisterRequest = operations['sw/register']['requestBody']['content']['application/json'];
export type SwRegisterResponse = operations['sw/register']['responses']['200']['content']['application/json'];
export type SwUnregisterRequest = operations['sw/unregister']['requestBody']['content']['application/json'];
export type TestRequest = operations['test']['requestBody']['content']['application/json'];
export type UsernameAvailableRequest = operations['username/available']['requestBody']['content']['application/json'];
export type UsernameAvailableResponse = operations['username/available']['responses']['200']['content']['application/json'];
export type UsersRequest = operations['users']['requestBody']['content']['application/json'];
export type UsersResponse = operations['users']['responses']['200']['content']['application/json'];
export type UsersClipsRequest = operations['users/clips']['requestBody']['content']['application/json'];
export type UsersClipsResponse = operations['users/clips']['responses']['200']['content']['application/json'];
export type UsersFollowersRequest = operations['users/followers']['requestBody']['content']['application/json'];
export type UsersFollowersResponse = operations['users/followers']['responses']['200']['content']['application/json'];
export type UsersFollowingRequest = operations['users/following']['requestBody']['content']['application/json'];
export type UsersFollowingResponse = operations['users/following']['responses']['200']['content']['application/json'];
export type UsersGalleryPostsRequest = operations['users/gallery/posts']['requestBody']['content']['application/json'];
export type UsersGalleryPostsResponse = operations['users/gallery/posts']['responses']['200']['content']['application/json'];
export type UsersGetFrequentlyRepliedUsersRequest = operations['users/get-frequently-replied-users']['requestBody']['content']['application/json'];
export type UsersGetFrequentlyRepliedUsersResponse = operations['users/get-frequently-replied-users']['responses']['200']['content']['application/json'];
export type UsersFeaturedNotesRequest = operations['users/featured-notes']['requestBody']['content']['application/json'];
export type UsersFeaturedNotesResponse = operations['users/featured-notes']['responses']['200']['content']['application/json'];
export type UsersGroupsCreateRequest = operations['users/groups/create']['requestBody']['content']['application/json'];
export type UsersGroupsCreateResponse = operations['users/groups/create']['responses']['200']['content']['application/json'];
export type UsersGroupsDeleteRequest = operations['users/groups/delete']['requestBody']['content']['application/json'];
export type UsersGroupsInvitationsAcceptRequest = operations['users/groups/invitations/accept']['requestBody']['content']['application/json'];
export type UsersGroupsInvitationsRejectRequest = operations['users/groups/invitations/reject']['requestBody']['content']['application/json'];
export type UsersGroupsInviteRequest = operations['users/groups/invite']['requestBody']['content']['application/json'];
export type UsersGroupsJoinedResponse = operations['users/groups/joined']['responses']['200']['content']['application/json'];
export type UsersGroupsLeaveRequest = operations['users/groups/leave']['requestBody']['content']['application/json'];
export type UsersGroupsOwnedResponse = operations['users/groups/owned']['responses']['200']['content']['application/json'];
export type UsersGroupsPullRequest = operations['users/groups/pull']['requestBody']['content']['application/json'];
export type UsersGroupsShowRequest = operations['users/groups/show']['requestBody']['content']['application/json'];
export type UsersGroupsShowResponse = operations['users/groups/show']['responses']['200']['content']['application/json'];
export type UsersGroupsTransferRequest = operations['users/groups/transfer']['requestBody']['content']['application/json'];
export type UsersGroupsTransferResponse = operations['users/groups/transfer']['responses']['200']['content']['application/json'];
export type UsersGroupsUpdateRequest = operations['users/groups/update']['requestBody']['content']['application/json'];
export type UsersGroupsUpdateResponse = operations['users/groups/update']['responses']['200']['content']['application/json'];
export type UsersListsCreateRequest = operations['users/lists/create']['requestBody']['content']['application/json'];
export type UsersListsCreateResponse = operations['users/lists/create']['responses']['200']['content']['application/json'];
export type UsersListsDeleteRequest = operations['users/lists/delete']['requestBody']['content']['application/json'];
export type UsersListsListRequest = operations['users/lists/list']['requestBody']['content']['application/json'];
export type UsersListsListResponse = operations['users/lists/list']['responses']['200']['content']['application/json'];
export type UsersListsPullRequest = operations['users/lists/pull']['requestBody']['content']['application/json'];
export type UsersListsPushRequest = operations['users/lists/push']['requestBody']['content']['application/json'];
export type UsersListsShowRequest = operations['users/lists/show']['requestBody']['content']['application/json'];
export type UsersListsShowResponse = operations['users/lists/show']['responses']['200']['content']['application/json'];
export type UsersListsFavoriteRequest = operations['users/lists/favorite']['requestBody']['content']['application/json'];
export type UsersListsUnfavoriteRequest = operations['users/lists/unfavorite']['requestBody']['content']['application/json'];
export type UsersListsUpdateRequest = operations['users/lists/update']['requestBody']['content']['application/json'];
export type UsersListsUpdateResponse = operations['users/lists/update']['responses']['200']['content']['application/json'];
export type UsersListsCreateFromPublicRequest = operations['users/lists/create-from-public']['requestBody']['content']['application/json'];
export type UsersListsCreateFromPublicResponse = operations['users/lists/create-from-public']['responses']['200']['content']['application/json'];
export type UsersListsUpdateMembershipRequest = operations['users/lists/update-membership']['requestBody']['content']['application/json'];
export type UsersListsGetMembershipsRequest = operations['users/lists/get-memberships']['requestBody']['content']['application/json'];
export type UsersNotesRequest = operations['users/notes']['requestBody']['content']['application/json'];
export type UsersNotesResponse = operations['users/notes']['responses']['200']['content']['application/json'];
export type UsersPagesRequest = operations['users/pages']['requestBody']['content']['application/json'];
export type UsersPagesResponse = operations['users/pages']['responses']['200']['content']['application/json'];
export type UsersFlashsRequest = operations['users/flashs']['requestBody']['content']['application/json'];
export type UsersFlashsResponse = operations['users/flashs']['responses']['200']['content']['application/json'];
export type UsersReactionsRequest = operations['users/reactions']['requestBody']['content']['application/json'];
export type UsersReactionsResponse = operations['users/reactions']['responses']['200']['content']['application/json'];
export type UsersRecommendationRequest = operations['users/recommendation']['requestBody']['content']['application/json'];
export type UsersRecommendationResponse = operations['users/recommendation']['responses']['200']['content']['application/json'];
export type UsersRelationRequest = operations['users/relation']['requestBody']['content']['application/json'];
export type UsersRelationResponse = operations['users/relation']['responses']['200']['content']['application/json'];
export type UsersReportAbuseRequest = operations['users/report-abuse']['requestBody']['content']['application/json'];
export type UsersSearchByUsernameAndHostRequest = operations['users/search-by-username-and-host']['requestBody']['content']['application/json'];
export type UsersSearchByUsernameAndHostResponse = operations['users/search-by-username-and-host']['responses']['200']['content']['application/json'];
export type UsersSearchRequest = operations['users/search']['requestBody']['content']['application/json'];
export type UsersSearchResponse = operations['users/search']['responses']['200']['content']['application/json'];
export type UsersShowRequest = operations['users/show']['requestBody']['content']['application/json'];
export type UsersShowResponse = operations['users/show']['responses']['200']['content']['application/json'];
export type UsersStatsRequest = operations['users/stats']['requestBody']['content']['application/json'];
export type UsersStatsResponse = operations['users/stats']['responses']['200']['content']['application/json'];
export type UsersAchievementsRequest = operations['users/achievements']['requestBody']['content']['application/json'];
export type UsersUpdateMemoRequest = operations['users/update-memo']['requestBody']['content']['application/json'];
export type UsersTranslateRequest = operations['users/translate']['requestBody']['content']['application/json'];
export type UsersTranslateResponse = operations['users/translate']['responses']['200']['content']['application/json'];
export type FetchRssRequest = operations['fetch-rss']['requestBody']['content']['application/json'];
export type FetchExternalResourcesRequest = operations['fetch-external-resources']['requestBody']['content']['application/json'];
export type RetentionResponse = operations['retention']['responses']['200']['content']['application/json'];

View file

@ -0,0 +1,42 @@
/*
* version: 4.6.0-beta.1
* generatedAt: 2023-12-05T08:05:02.072Z
*/
import { components } from './types.js';
export type Error = components['schemas']['Error'];
export type UserLite = components['schemas']['UserLite'];
export type UserDetailedNotMeOnly = components['schemas']['UserDetailedNotMeOnly'];
export type MeDetailedOnly = components['schemas']['MeDetailedOnly'];
export type UserDetailedNotMe = components['schemas']['UserDetailedNotMe'];
export type MeDetailed = components['schemas']['MeDetailed'];
export type UserDetailed = components['schemas']['UserDetailed'];
export type User = components['schemas']['User'];
export type UserList = components['schemas']['UserList'];
export type UserGroup = components['schemas']['UserGroup'];
export type Announcement = components['schemas']['Announcement'];
export type App = components['schemas']['App'];
export type MessagingMessage = components['schemas']['MessagingMessage'];
export type Note = components['schemas']['Note'];
export type NoteReaction = components['schemas']['NoteReaction'];
export type NoteFavorite = components['schemas']['NoteFavorite'];
export type Notification = components['schemas']['Notification'];
export type DriveFile = components['schemas']['DriveFile'];
export type DriveFolder = components['schemas']['DriveFolder'];
export type Following = components['schemas']['Following'];
export type Muting = components['schemas']['Muting'];
export type RenoteMuting = components['schemas']['RenoteMuting'];
export type Blocking = components['schemas']['Blocking'];
export type Hashtag = components['schemas']['Hashtag'];
export type InviteCode = components['schemas']['InviteCode'];
export type Page = components['schemas']['Page'];
export type Channel = components['schemas']['Channel'];
export type QueueCount = components['schemas']['QueueCount'];
export type Antenna = components['schemas']['Antenna'];
export type Clip = components['schemas']['Clip'];
export type FederationInstance = components['schemas']['FederationInstance'];
export type GalleryPost = components['schemas']['GalleryPost'];
export type EmojiSimple = components['schemas']['EmojiSimple'];
export type EmojiDetailed = components['schemas']['EmojiDetailed'];
export type Flash = components['schemas']['Flash'];
export type Signin = components['schemas']['Signin'];

File diff suppressed because it is too large Load diff

View file

@ -1,469 +1,12 @@
import { ModerationLogPayloads, notificationTypes } from './consts.js';
import { ModerationLogPayloads } from './consts.js';
import { Announcement, EmojiDetailed, Page, User, UserDetailed } from './autogen/models';
export * from './autogen/entities';
export * from './autogen/models';
export type ID = string;
export type DateString = string;
type TODO = Record<string, any>;
// NOTE: 極力この型を使うのは避け、UserLite か UserDetailed か明示するように
export type User = UserLite | UserDetailed;
export type UserLite = {
id: ID;
username: string;
host: string | null;
name: string | null;
onlineStatus: 'online' | 'active' | 'offline' | 'unknown';
avatarUrl: string;
avatarBlurhash: string;
avatarDecorations: {
id: ID;
url: string;
angle?: number;
flipH?: boolean;
scale?: number;
moveX?: number;
moveY?: number;
opacity?: number;
}[];
emojis: {
name: string;
url: string;
}[];
instance?: {
name: Instance['name'];
softwareName: Instance['softwareName'];
softwareVersion: Instance['softwareVersion'];
iconUrl: Instance['iconUrl'];
faviconUrl: Instance['faviconUrl'];
themeColor: Instance['themeColor'];
};
isCat?: boolean;
isBot?: boolean;
};
export type UserDetailed = UserLite & {
alsoKnownAs: string[];
bannerBlurhash: string | null;
bannerColor: string | null;
bannerUrl: string | null;
birthday: string | null;
createdAt: DateString;
description: string | null;
ffVisibility: 'public' | 'followers' | 'private';
fields: {name: string; value: string}[];
verifiedLinks: string[];
followersCount: number;
followingCount: number;
hasPendingFollowRequestFromYou: boolean;
hasPendingFollowRequestToYou: boolean;
isAdmin: boolean;
isBlocked: boolean;
isBlocking: boolean;
isBot: boolean;
isCat: boolean;
isFollowed: boolean;
isFollowing: boolean;
isLocked: boolean;
isModerator: boolean;
isMuted: boolean;
isSilenced: boolean;
isSuspended: boolean;
lang: string | null;
lastFetchedAt?: DateString;
location: string | null;
movedTo: string;
notesCount: number;
pinnedNoteIds: ID[];
pinnedNotes: Note[];
pinnedPage: Page | null;
pinnedPageId: string | null;
publicReactions: boolean;
securityKeys: boolean;
twoFactorEnabled: boolean;
updatedAt: DateString | null;
uri: string | null;
url: string | null;
notify: 'normal' | 'none';
};
export type UserGroup = TODO;
export type UserList = {
id: ID;
createdAt: DateString;
name: string;
userIds: User['id'][];
};
export type MeDetailed = UserDetailed & {
avatarId: DriveFile['id'];
bannerId: DriveFile['id'];
autoAcceptFollowed: boolean;
alwaysMarkNsfw: boolean;
carefulBot: boolean;
emailNotificationTypes: string[];
hasPendingReceivedFollowRequest: boolean;
hasUnreadAnnouncement: boolean;
hasUnreadAntenna: boolean;
hasUnreadMentions: boolean;
hasUnreadMessagingMessage: boolean;
hasUnreadNotification: boolean;
hasUnreadSpecifiedNotes: boolean;
unreadNotificationsCount: number;
hideOnlineStatus: boolean;
injectFeaturedNote: boolean;
integrations: Record<string, any>;
isDeleted: boolean;
isExplorable: boolean;
mutedWords: (string[] | string)[];
hardMutedWords: (string[] | string)[];
notificationRecieveConfig: {
[notificationType in typeof notificationTypes[number]]?: {
type: 'all';
} | {
type: 'never';
} | {
type: 'following';
} | {
type: 'follower';
} | {
type: 'mutualFollow';
} | {
type: 'list';
userListId: string;
};
};
noCrawle: boolean;
receiveAnnouncementEmail: boolean;
usePasswordLessLogin: boolean;
unreadAnnouncements: Announcement[];
twoFactorBackupCodesStock: 'full' | 'partial' | 'none';
[other: string]: any;
};
export type MeDetailedWithSecret = MeDetailed & {
email: string;
emailVerified: boolean;
securityKeysList: {
id: string;
name: string;
lastUsed: string;
}[];
};
export type MeSignup = MeDetailedWithSecret & {
token: string;
};
export type DriveFile = {
id: ID;
createdAt: DateString;
isSensitive: boolean;
name: string;
thumbnailUrl: string;
url: string;
type: string;
size: number;
md5: string;
blurhash: string;
comment: string | null;
properties: Record<string, any>;
};
export type DriveFolder = TODO;
export type GalleryPost = {
id: ID;
createdAt: DateString;
updatedAt: DateString;
userId: User['id'];
user: User;
title: string;
description: string | null;
fileIds: DriveFile['id'][];
files: DriveFile[];
isSensitive: boolean;
likedCount: number;
isLiked?: boolean;
};
export type Note = {
id: ID;
createdAt: DateString;
updatedAt?: DateString | null;
updatedAtHistory: DateString[] | null;
noteEditHistory: string[];
text: string | null;
cw: string | null;
user: User;
userId: User['id'];
reply?: Note;
replyId: Note['id'];
renote?: Note;
renoteId: Note['id'];
event?: {
title: string,
start: DateString,
end: DateString | null,
metadata: Record<string, string>,
};
files: DriveFile[];
fileIds: DriveFile['id'][];
visibility: 'public' | 'home' | 'followers' | 'specified';
visibleUserIds?: User['id'][];
channel?: Channel;
channelId?: Channel['id'];
localOnly?: boolean;
myReaction?: string;
reactions: Record<string, number>;
renoteCount: number;
repliesCount: number;
clippedCount?: number;
poll?: {
expiresAt: DateString | null;
multiple: boolean;
choices: {
isVoted: boolean;
text: string;
votes: number;
}[];
};
emojis: {
name: string;
url: string;
}[];
uri?: string;
url?: string;
isHidden?: boolean;
};
export type NoteReaction = {
id: ID;
createdAt: DateString;
user: UserLite;
type: string;
};
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: 'note';
user: User;
userId: User['id'];
note: Note;
} | {
type: 'pollEnded';
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: 'achievementEarned';
achievement: string;
} | {
type: 'app';
header?: string | null;
body: string;
icon?: string | null;
} | {
type: 'test';
});
export type MessagingMessage = {
id: ID;
createdAt: DateString;
file: DriveFile | null;
fileId: DriveFile['id'] | null;
isRead: boolean;
reads: User['id'][];
text: string | null;
user: User;
userId: User['id'];
recipient?: User | null;
recipientId: User['id'] | null;
group?: UserGroup | null;
groupId: UserGroup['id'] | null;
};
export type CustomEmoji = {
id: string;
name: string;
url: string;
category: string;
aliases: string[];
};
export type LiteInstanceMetadata = {
maintainerName: string | null;
maintainerEmail: string | null;
version: string;
basedMisskeyVersion: string;
name: string | null;
shortName: string | null;
uri: string;
description: string | null;
langs: string[];
tosUrl: string | null;
repositoryUrl: string;
feedbackUrl: string;
impressumUrl: string | null;
privacyPolicyUrl: string | null;
disableRegistration: boolean;
disableLocalTimeline: boolean;
disableGlobalTimeline: boolean;
driveCapacityPerLocalUserMb: number;
driveCapacityPerRemoteUserMb: number;
emailRequiredForSignup: boolean;
enableHcaptcha: boolean;
hcaptchaSiteKey: string | null;
enableRecaptcha: boolean;
recaptchaSiteKey: string | null;
enableTurnstile: boolean;
turnstileSiteKey: string | null;
swPublickey: string | null;
themeColor: string | null;
mascotImageUrl: string | null;
bannerUrl: string | null;
serverErrorImageUrl: string | null;
infoImageUrl: string | null;
notFoundImageUrl: string | null;
iconUrl: string | null;
backgroundImageUrl: string | null;
logoImageUrl: string | null;
maxNoteTextLength: number;
enableEmail: boolean;
enableTwitterIntegration: boolean;
enableGithubIntegration: boolean;
enableDiscordIntegration: boolean;
enableServiceWorker: boolean;
emojis: CustomEmoji[];
defaultDarkTheme: string | null;
defaultLightTheme: string | null;
ads: {
id: ID;
ratio: number;
place: string;
url: string;
imageUrl: string;
}[];
notesPerOneAd: number;
translatorAvailable: boolean;
serverRules: string[];
};
export type DetailedInstanceMetadata = LiteInstanceMetadata & {
pinnedPages: string[];
pinnedClipId: string | null;
cacheRemoteFiles: boolean;
cacheRemoteSensitiveFiles: boolean;
requireSetup: boolean;
proxyAccountName: string | null;
features: Record<string, any>;
};
export type InstanceMetadata = LiteInstanceMetadata | DetailedInstanceMetadata;
export type AdminInstanceMetadata = DetailedInstanceMetadata & {
// TODO: There are more fields.
blockedHosts: string[];
silencedHosts: string[];
app192IconUrl: string | null;
app512IconUrl: string | null;
manifestJsonOverride: string;
};
export type ServerInfo = {
machine: string;
cpu: {
model: string;
cores: number;
};
mem: {
total: number;
};
fs: {
total: number;
used: number;
};
};
export type Stats = {
notesCount: number;
originalNotesCount: number;
usersCount: number;
originalUsersCount: number;
instances: number;
driveUsageLocal: number;
driveUsageRemote: number;
};
export type Page = {
id: ID;
createdAt: DateString;
updatedAt: DateString;
userId: User['id'];
user: User;
content: Record<string, any>[];
variables: Record<string, any>[];
title: string;
name: string;
summary: string | null;
hideTitleWhenPinned: boolean;
alignCenter: boolean;
font: string;
script: string;
eyeCatchingImageId: DriveFile['id'] | null;
eyeCatchingImage: DriveFile | null;
attachedFiles: any;
likedCount: number;
isLiked?: boolean;
};
export type PageEvent = {
pageId: Page['id'];
event: string;
@ -472,166 +15,6 @@ export type PageEvent = {
user: User;
};
export type Announcement = {
id: ID;
createdAt: DateString;
updatedAt: DateString | null;
text: string;
title: string;
imageUrl: string | null;
display: 'normal' | 'banner' | 'dialog';
icon: 'info' | 'warning' | 'error' | 'success';
needConfirmationToRead: boolean;
forYou: boolean;
isRead?: boolean;
};
export type Antenna = {
id: ID;
createdAt: DateString;
name: string;
keywords: string[][]; // TODO
excludeKeywords: string[][]; // TODO
src: 'home' | 'all' | 'users' | 'list' | 'group';
userListId: ID | null; // TODO
userGroupId: ID | null; // TODO
users: string[]; // TODO
caseSensitive: boolean;
localOnly: boolean;
notify: boolean;
withReplies: boolean;
withFile: boolean;
hasUnreadNote: boolean;
};
export type App = TODO;
export type AuthSession = {
id: ID;
app: App;
token: string;
};
export type Ad = TODO;
export type Clip = TODO;
export type NoteFavorite = {
id: ID;
createdAt: DateString;
noteId: Note['id'];
note: Note;
};
export type FollowRequest = {
id: ID;
follower: User;
followee: User;
};
export type Channel = {
id: ID;
lastNotedAt: Date | null;
userId: User['id'] | null;
user: User | null;
name: string;
description: string | null;
bannerId: DriveFile['id'] | null;
banner: DriveFile | null;
pinnedNoteIds: string[];
color: string;
isArchived: boolean;
notesCount: number;
usersCount: number;
isSensitive: boolean;
allowRenoteToExternal: boolean;
};
export type Following = {
id: ID;
createdAt: DateString;
followerId: User['id'];
followeeId: User['id'];
};
export type FollowingFolloweePopulated = Following & {
followee: UserDetailed;
};
export type FollowingFollowerPopulated = Following & {
follower: UserDetailed;
};
export type Blocking = {
id: ID;
createdAt: DateString;
blockeeId: User['id'];
blockee: UserDetailed;
};
export type Instance = {
id: ID;
firstRetrievedAt: DateString;
host: string;
usersCount: number;
notesCount: number;
followingCount: number;
followersCount: number;
driveUsage: number;
driveFiles: number;
latestRequestSentAt: DateString | null;
latestStatus: number | null;
latestRequestReceivedAt: DateString | null;
lastCommunicatedAt: DateString;
isNotResponding: boolean;
isSuspended: boolean;
isSilenced: boolean;
isBlocked: boolean;
softwareName: string | null;
softwareVersion: string | null;
openRegistrations: boolean | null;
name: string | null;
description: string | null;
maintainerName: string | null;
maintainerEmail: string | null;
iconUrl: string | null;
faviconUrl: string | null;
themeColor: string | null;
infoUpdatedAt: DateString | null;
};
export type Signin = {
id: ID;
createdAt: DateString;
ip: string;
headers: Record<string, any>;
success: boolean;
};
export type Invite = {
id: ID;
code: string;
expiresAt: DateString | null;
createdAt: DateString;
createdBy: UserLite | null;
usedBy: UserLite | null;
usedAt: DateString | null;
used: boolean;
}
export type InviteLimit = {
remaining: number;
}
export type UserSorting =
| '+follower'
| '-follower'
| '+createdAt'
| '-createdAt'
| '+updatedAt'
| '-updatedAt';
export type OriginType = 'combined' | 'local' | 'remote';
export type ModerationLog = {
id: ID;
createdAt: DateString;
@ -749,3 +132,54 @@ export type ModerationLog = {
type: 'unsetUserBanner';
info: ModerationLogPayloads['unsetUserBanner'];
});
export type ServerStats = {
cpu: number;
mem: {
used: number;
active: number;
};
net: {
rx: number;
tx: number;
};
fs: {
r: number;
w: number;
}
};
export type ServerStatsLog = string[];
export type QueueStats = {
deliver: {
activeSincePrevTick: number;
active: number;
waiting: number;
delayed: number;
};
inbox: {
activeSincePrevTick: number;
active: number;
waiting: number;
delayed: number;
};
};
export type QueueStatsLog = string[];
export type EmojiAdded = {
emoji: EmojiDetailed
};
export type EmojiUpdated = {
emojis: EmojiDetailed[]
};
export type EmojiDeleted = {
emojis: EmojiDetailed[]
};
export type AnnouncementCreated = {
announcement: Announcement;
};

View file

@ -1,6 +1,25 @@
import type { Antenna, CustomEmoji, DriveFile, MeDetailed, MessagingMessage, Note, Notification, PageEvent, User, UserGroup } from './entities.js';
type FIXME = any;
import {
Antenna,
DriveFile,
DriveFolder,
MeDetailed,
Note,
Notification,
Signin,
User,
UserGroup,
} from './autogen/models.js';
import {
AnnouncementCreated,
EmojiAdded, EmojiDeleted,
EmojiUpdated,
MessagingMessage,
PageEvent,
QueueStats,
QueueStatsLog,
ServerStats,
ServerStatsLog,
} from './entities.js';
export type Channels = {
main: {
@ -29,9 +48,7 @@ export type Channels = {
unreadAntenna: (payload: Antenna) => void;
readAllAnnouncements: () => void;
myTokenRegenerated: () => void;
reversiNoInvites: () => void;
reversiInvited: (payload: FIXME) => void;
signin: (payload: FIXME) => void;
signin: (payload: Signin) => void;
registryUpdated: (payload: {
scope?: string[];
key: string;
@ -39,32 +56,52 @@ export type Channels = {
}) => void;
driveFileCreated: (payload: DriveFile) => void;
readAntenna: (payload: Antenna) => void;
receiveFollowRequest: (payload: User) => void;
announcementCreated: (payload: AnnouncementCreated) => void;
};
receives: null;
};
homeTimeline: {
params: null;
params: {
withRenotes?: boolean;
withFiles?: boolean;
withCats?: boolean;
};
events: {
note: (payload: Note) => void;
};
receives: null;
};
localTimeline: {
params: null;
params: {
withRenotes?: boolean;
withReplies?: boolean;
withFiles?: boolean;
withCats?: boolean;
};
events: {
note: (payload: Note) => void;
};
receives: null;
};
hybridTimeline: {
params: null;
params: {
withRenotes?: boolean;
withReplies?: boolean;
withFiles?: boolean;
withCats?: boolean;
};
events: {
note: (payload: Note) => void;
};
receives: null;
};
globalTimeline: {
params: null;
params: {
withRenotes?: boolean;
withFiles?: boolean;
withCats?: boolean;
};
events: {
note: (payload: Note) => void;
};
@ -87,10 +124,70 @@ export type Channels = {
};
};
};
userList: {
params: {
listId: string;
withFiles?: boolean;
withCats?: boolean;
};
events: {
note: (payload: Note) => void;
};
receives: null;
};
hashtag: {
params: {
q?: string;
};
events: {
note: (payload: Note) => void;
};
receives: null;
};
roleTimeline: {
params: {
roleId: string;
};
events: {
note: (payload: Note) => void;
};
receives: null;
};
antenna: {
params: {
antennaId: string;
};
events: {
note: (payload: Note) => void;
};
receives: null;
};
channel: {
params: {
channelId: string;
};
events: {
note: (payload: Note) => void;
};
receives: null;
};
drive: {
params: null;
events: {
fileCreated: (payload: DriveFile) => void;
fileDeleted: (payload: DriveFile['id']) => void;
fileUpdated: (payload: DriveFile) => void;
folderCreated: (payload: DriveFolder) => void;
folderDeleted: (payload: DriveFolder['id']) => void;
folderUpdated: (payload: DriveFile) => void;
};
receives: null;
};
serverStats: {
params: null;
events: {
stats: (payload: FIXME) => void;
stats: (payload: ServerStats) => void;
statsLog: (payload: ServerStatsLog) => void;
};
receives: {
requestLog: {
@ -102,7 +199,8 @@ export type Channels = {
queueStats: {
params: null;
events: {
stats: (payload: FIXME) => void;
stats: (payload: QueueStats) => void;
statsLog: (payload: QueueStatsLog) => void;
};
receives: {
requestLog: {
@ -111,37 +209,45 @@ export type Channels = {
};
};
};
admin: {
params: null;
events: {
newAbuseUserReport: {
id: string;
targetUserId: string;
reporterId: string;
comment: string;
}
};
receives: null;
}
};
export type NoteUpdatedEvent = {
id: Note['id'];
type: 'reacted';
body: {
reaction: string;
emoji: string | null;
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: 'updated';
body: {
cw: string | null;
text: string;
};
} | {
id: Note['id'];
type: 'pollVoted';
body: {
choice: number;
@ -151,7 +257,8 @@ export type NoteUpdatedEvent = {
export type BroadcastEvents = {
noteUpdated: (payload: NoteUpdatedEvent) => void;
emojiAdded: (payload: {
emoji: CustomEmoji;
}) => void;
emojiAdded: (payload: EmojiAdded) => void;
emojiUpdated: (payload: EmojiUpdated) => void;
emojiDeleted: (payload: EmojiDeleted) => void;
announcementCreated: (payload: AnnouncementCreated) => void;
};

View file

@ -8,7 +8,7 @@ describe('API', () => {
credential: 'TOKEN'
});
const res = await cli.request('meta', { detail: true });
expectType<Misskey.entities.DetailedInstanceMetadata>(res);
expectType<Misskey.entities.MetaResponse>(res);
});
test('conditional respose type (meta)', async () => {
@ -18,16 +18,16 @@ describe('API', () => {
});
const res = await cli.request('meta', { detail: true });
expectType<Misskey.entities.DetailedInstanceMetadata>(res);
expectType<Misskey.entities.MetaResponse>(res);
const res2 = await cli.request('meta', { detail: false });
expectType<Misskey.entities.LiteInstanceMetadata>(res2);
expectType<Misskey.entities.MetaResponse>(res2);
const res3 = await cli.request('meta', { });
expectType<Misskey.entities.LiteInstanceMetadata>(res3);
expectType<Misskey.entities.MetaResponse>(res3);
const res4 = await cli.request('meta', { detail: true as boolean });
expectType<Misskey.entities.LiteInstanceMetadata | Misskey.entities.DetailedInstanceMetadata>(res4);
expectType<Misskey.entities.MetaResponse>(res4);
});
test('conditional respose type (users/show)', async () => {

View file

@ -1,5 +1,5 @@
{
"$schema": "http://json.schemastore.org/tsconfig",
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"target": "ES2022",
"module": "nodenext",

View file

@ -89,7 +89,6 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
api("users/notes", {
userId: props.user.id,
fileType: image,
excludeNsfw: defaultStore.state.nsfw !== "ignore",
limit: 10
}).then((notes) => {
for (const note of notes) {
@ -198,7 +197,6 @@ const _sfc_main = defineComponent({
api("users/notes", {
userId: props.user.id,
fileType: image,
excludeNsfw: defaultStore.state.nsfw !== "ignore",
limit: 10
}).then(notes => {
for (const note of notes) {

View file

@ -19,6 +19,7 @@ import { claimAchievement, claimedAchievements } from '@/scripts/achievements.js
import { mainRouter } from '@/router.js';
import { initializeSw } from '@/scripts/initialize-sw.js';
import { deckStore } from '@/ui/deck/deck-store.js';
import { emojiPicker } from '@/scripts/emoji-picker.js';
import { userName } from '@/filters/user.js';
import { vibrate } from '@/scripts/vibrate.js';
@ -33,6 +34,7 @@ export async function mainBoot() {
));
reactionPicker.init();
emojiPicker.init();
if (isClientUpdated && $i) {
popup(defineAsyncComponent(() => import('@/components/MkUpdated.vue')), {}, {}, 'closed');

View file

@ -37,7 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</section>
<div v-if="tab === 'index'" class="group index">
<section v-if="showPinned">
<section v-if="showPinned && pinned.length > 0">
<div class="body">
<button
v-for="emoji in pinned"
@ -138,7 +138,7 @@ const searchEl = shallowRef<HTMLInputElement>();
const emojisEl = shallowRef<HTMLDivElement>();
const {
reactions: pinned,
reactions: pinnedReactions,
reactionPickerSize,
reactionPickerWidth,
reactionPickerHeight,
@ -146,11 +146,12 @@ const {
recentlyUsedEmojis,
} = defaultStore.reactiveState;
const pinned = computed(() => props.asReactionPicker ? pinnedReactions.value : []); // TODO: pinned
const size = computed(() => props.asReactionPicker ? reactionPickerSize.value : 1);
const width = computed(() => props.asReactionPicker ? reactionPickerWidth.value : 3);
const height = computed(() => props.asReactionPicker ? reactionPickerHeight.value : 2);
const q = ref<string>('');
const searchResultCustom = ref<Misskey.entities.CustomEmoji[]>([]);
const searchResultCustom = ref<Misskey.entities.EmojiSimple[]>([]);
const searchResultUnicode = ref<UnicodeEmojiDef[]>([]);
const tab = ref<'index' | 'custom' | 'unicode' | 'tags'>('index');
@ -197,7 +198,7 @@ watch(q, () => {
const searchCustom = () => {
const max = 100;
const emojis = customEmojis.value;
const matches = new Set<Misskey.entities.CustomEmoji>();
const matches = new Set<Misskey.entities.EmojiSimple>();
const exactMatch = emojis.find(emoji => emoji.name === newQ);
if (exactMatch) matches.add(exactMatch);
@ -327,7 +328,7 @@ watch(q, () => {
searchResultUnicode.value = Array.from(searchUnicode());
});
function filterAvailable(emoji: Misskey.entities.CustomEmoji): boolean {
function filterAvailable(emoji: Misskey.entities.EmojiSimple): boolean {
return (emoji.roleIdsThatCanBeUsedThisEmojiAsReaction == null || emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0) || ($i && $i.roles.some(r => emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.includes(r.id)));
}
@ -344,7 +345,7 @@ function reset() {
q.value = '';
}
function getKey(emoji: string | Misskey.entities.CustomEmoji | UnicodeEmojiDef): string {
function getKey(emoji: string | Misskey.entities.EmojiSimple | UnicodeEmojiDef): string {
return typeof emoji === 'string' ? emoji : 'char' in emoji ? emoji.char : `:${emoji.name}:`;
}

View file

@ -31,20 +31,21 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { shallowRef } from 'vue';
import MkModal from '@/components/MkModal.vue';
import MkEmojiPicker from '@/components/MkEmojiPicker.vue';
import { defaultStore } from '@/store.js';
withDefaults(defineProps<{
const props = withDefaults(defineProps<{
manualShowing?: boolean | null;
src?: HTMLElement;
showPinned?: boolean;
asReactionPicker?: boolean;
choseAndClose?: boolean;
}>(), {
manualShowing: null,
showPinned: true,
asReactionPicker: false,
choseAndClose: true,
});
const emit = defineEmits<{
@ -53,21 +54,23 @@ const emit = defineEmits<{
(ev: 'closed'): void;
}>();
const modal = shallowRef<InstanceType<typeof MkModal>>();
const picker = shallowRef<InstanceType<typeof MkEmojiPicker>>();
const modal = $shallowRef<InstanceType<typeof MkModal>>();
const picker = $shallowRef<InstanceType<typeof MkEmojiPicker>>();
function chosen(emoji: any) {
emit('done', emoji);
modal.value?.close();
if (props.choseAndClose) {
modal?.close();
}
}
function opening() {
picker.value?.reset();
picker.value?.focus();
picker?.reset();
picker?.focus();
//
setTimeout(() => {
picker.value?.focus();
picker?.focus();
}, 10);
}
</script>

View file

@ -12,7 +12,7 @@ import { ref } from 'vue';
import * as Misskey from 'cherrypick-js';
import * as os from '@/os.js';
const meta = ref<Misskey.entities.DetailedInstanceMetadata>();
const meta = ref<Misskey.entities.MetaResponse>();
os.api('meta', { detail: true }).then(gotMeta => {
meta.value = gotMeta;

View file

@ -21,15 +21,15 @@ import * as os from '@/os.js';
import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js';
const props = defineProps<{
instance: Misskey.entities.Instance;
instance: Misskey.entities.FederationInstance;
}>();
let chartValues = $ref<number[] | null>(null);
os.apiGet('charts/instance', { host: props.instance.host, limit: 16 + 1, span: 'day' }).then(res => {
//
res.requests.received.splice(0, 1);
chartValues = res.requests.received;
res['requests.received'].splice(0, 1);
chartValues = res['requests.received'];
});
function getInstanceIcon(instance): string {

View file

@ -67,7 +67,7 @@ import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
const props = defineProps<{
invite: Misskey.entities.Invite;
invite: Misskey.entities.InviteCode;
moderator?: boolean;
}>();

View file

@ -203,7 +203,7 @@ function focusDown() {
}
function switchItem(item: MenuSwitch & { ref: any }) {
if (item.disabled) return;
if (typeof item.disabled === 'boolean' ? item.disabled : item.disabled.value) return;
item.ref = !item.ref;
}

View file

@ -206,6 +206,7 @@ async function init(): Promise<void> {
await os.api(props.pagination.endpoint, {
...params,
limit: props.pagination.limit ?? 10,
allowPartial: true,
}).then(res => {
for (let i = 0; i < res.length; i++) {
const item = res[i];

View file

@ -129,6 +129,7 @@ import { deepClone } from '@/scripts/clone.js';
import MkRippleEffect from '@/components/MkRippleEffect.vue';
import { miLocalStorage } from '@/local-storage.js';
import { claimAchievement } from '@/scripts/achievements.js';
import { emojiPicker } from '@/scripts/emoji-picker.js';
import { vibrate } from '@/scripts/vibrate.js';
import * as sound from '@/scripts/sound.js';
@ -392,8 +393,8 @@ function checkMissingMention() {
return;
}
}
hasNotSpecifiedMentions = false;
}
hasNotSpecifiedMentions = false;
}
function addMissingMention() {
@ -911,7 +912,15 @@ function insertMention() {
}
async function insertEmoji(ev: MouseEvent) {
os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textareaEl);
emojiPicker.show(
ev.currentTarget ?? ev.target,
emoji => {
insertTextAtCursor(textareaEl, emoji);
},
() => {
focus();
},
);
}
function showActions(ev) {

View file

@ -149,6 +149,7 @@ import { deepClone } from '@/scripts/clone.js';
import MkRippleEffect from '@/components/MkRippleEffect.vue';
import { miLocalStorage } from '@/local-storage.js';
import { claimAchievement } from '@/scripts/achievements.js';
import { emojiPicker } from '@/scripts/emoji-picker.js';
import { vibrate } from '@/scripts/vibrate.js';
import XSigninDialog from '@/components/MkSigninDialog.vue';
import * as sound from '@/scripts/sound.js';
@ -413,8 +414,8 @@ function checkMissingMention() {
return;
}
}
hasNotSpecifiedMentions = false;
}
hasNotSpecifiedMentions = false;
}
function addMissingMention() {
@ -932,7 +933,15 @@ function insertMention() {
}
async function insertEmoji(ev: MouseEvent) {
os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textareaEl);
emojiPicker.show(
ev.currentTarget ?? ev.target,
emoji => {
insertTextAtCursor(textareaEl, emoji);
},
() => {
focus();
},
);
}
function showActions(ev) {

View file

@ -69,15 +69,14 @@ import number from '@/filters/number.js';
import MkNumber from '@/components/MkNumber.vue';
import XActiveUsersChart from '@/components/MkVisitorDashboard.ActiveUsersChart.vue';
let meta = $ref<Misskey.entities.Instance>();
let stats = $ref(null);
let meta = $ref<Misskey.entities.MetaResponse | null>(null);
let stats = $ref<Misskey.entities.StatsResponse | null>(null);
os.api('meta', { detail: true }).then(_meta => {
meta = _meta;
});
os.api('stats', {
}).then((res) => {
os.api('stats', {}).then((res) => {
stats = res;
});

View file

@ -251,11 +251,17 @@ export default function(props: MfmProps) {
case 'ruby': {
if (token.children.length === 1) {
const child = token.children[0];
const text = child.type === 'text' ? child.props.text : '';
let text = child.type === 'text' ? child.props.text : '';
if (!disableNyaize && shouldNyaize) {
text = doNyaize(text);
}
return h('ruby', {}, [text.split(' ')[0], h('rt', text.split(' ')[1])]);
} else {
const rt = token.children.at(-1)!;
const text = rt.type === 'text' ? rt.props.text : '';
let text = rt.type === 'text' ? rt.props.text : '';
if (!disableNyaize && shouldNyaize) {
text = doNyaize(text);
}
return h('ruby', {}, [...genEl(token.children.slice(0, token.children.length - 1), scale), h('rt', text.trim())]);
}
}

View file

@ -10,7 +10,7 @@ import { useStream } from '@/stream.js';
import { get, set, exist } from '@/scripts/idb-proxy.js';
const storageCache = await get('emojis');
export const customEmojis = shallowRef<Misskey.entities.CustomEmoji[]>(Array.isArray(storageCache) ? storageCache : []);
export const customEmojis = shallowRef<Misskey.entities.EmojiSimple[]>(Array.isArray(storageCache) ? storageCache : []);
export const customEmojiCategories = computed<[ ...string[], null ]>(() => {
const categories = new Set<string>();
for (const emoji of customEmojis.value) {
@ -21,7 +21,7 @@ export const customEmojiCategories = computed<[ ...string[], null ]>(() => {
return markRaw([...Array.from(categories), null]);
});
export const customEmojisMap = new Map<string, Misskey.entities.CustomEmoji>();
export const customEmojisMap = new Map<string, Misskey.entities.EmojiSimple>();
watch(customEmojis, emojis => {
customEmojisMap.clear();
for (const emoji of emojis) {
@ -38,7 +38,7 @@ stream.on('emojiAdded', emojiData => {
});
stream.on('emojiUpdated', emojiData => {
customEmojis.value = customEmojis.value.map(item => emojiData.emojis.find(search => search.name === item.name) as Misskey.entities.CustomEmoji ?? item);
customEmojis.value = customEmojis.value.map(item => emojiData.emojis.find(search => search.name === item.name) as Misskey.entities.EmojiSimple ?? item);
set('emojis', customEmojis.value);
});

View file

@ -13,7 +13,7 @@ export const acct = (user: Misskey.Acct) => {
export const userName = (user: Misskey.entities.User) => {
if (!defaultStore.state.nicknameEnabled) {
return user.name || user.username;
return user.name ?? user.username;
}
return defaultStore.reactiveState.nicknameMap.value[user.id] || user.name || user.username;
};

View file

@ -15,7 +15,7 @@ const cached = miLocalStorage.getItem('instance');
// TODO: instanceをリアクティブにするかは再考の余地あり
export const instance: Misskey.entities.InstanceMetadata = reactive(cached ? JSON.parse(cached) : {
export const instance: Misskey.entities.MetaResponse = reactive(cached ? JSON.parse(cached) : {
// TODO: set default values
});

View file

@ -44,7 +44,7 @@ const props = withDefaults(defineProps<{
let loaded = $ref(false);
let serverIsDead = $ref(false);
let meta = $ref<Misskey.entities.LiteInstanceMetadata | null>(null);
let meta = $ref<Misskey.entities.MetaResponse | null>(null);
os.api('meta', {
detail: false,

View file

@ -48,7 +48,7 @@ import { $i } from '@/account.js';
const customEmojiTags = getCustomEmojiTags();
let q = $ref('');
let searchEmojis = $ref<Misskey.entities.CustomEmoji[]>(null);
let searchEmojis = $ref<Misskey.entities.EmojiSimple[]>(null);
let selectedTags = $ref(new Set());
function search() {

View file

@ -27,7 +27,7 @@ import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
const props = defineProps<{
session: Misskey.entities.AuthSession;
session: Misskey.entities.AuthSessionShowResponse;
}>();
const emit = defineEmits<{

View file

@ -56,7 +56,7 @@ const props = defineProps<{
}>();
let state = $ref<'waiting' | 'accepted' | 'fetch-session-error' | 'denied' | null>(null);
let session = $ref<Misskey.entities.AuthSession | null>(null);
let session = $ref<Misskey.entities.AuthSessionShowResponse | null>(null);
function accepted() {
state = 'accepted';

View file

@ -155,7 +155,7 @@ const edit = (emoji) => {
}, 'closed');
};
const im = (emoji) => {
const importEmoji = (emoji) => {
os.apiWithDialog('admin/emoji/copy', {
emojiId: emoji.id,
});
@ -168,7 +168,7 @@ const remoteMenu = (emoji, ev: MouseEvent) => {
}, {
text: i18n.ts.import,
icon: 'ti ti-plus',
action: () => { im(emoji); },
action: () => { importEmoji(emoji); },
}], ev.currentTarget ?? ev.target);
};

View file

@ -144,8 +144,8 @@ const props = defineProps<{
let tab = $ref('overview');
let chartSrc = $ref('instance-requests');
let meta = $ref<Misskey.entities.AdminInstanceMetadata | null>(null);
let instance = $ref<Misskey.entities.Instance | null>(null);
let meta = $ref<Misskey.entities.AdminMetaResponse | null>(null);
let instance = $ref<Misskey.entities.FederationInstance | null>(null);
let suspended = $ref(false);
let isBlocked = $ref(false);
let isSilenced = $ref(false);
@ -169,10 +169,10 @@ async function fetch(): Promise<void> {
instance = await os.api('federation/show-instance', {
host: props.host,
});
suspended = instance.isSuspended;
isBlocked = instance.isBlocked;
isSilenced = instance.isSilenced;
faviconUrl = getProxiedImageUrlNullable(instance.faviconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.iconUrl, 'preview');
suspended = instance?.isSuspended ?? false;
isBlocked = instance?.isBlocked ?? false;
isSilenced = instance?.isSilenced ?? false;
faviconUrl = getProxiedImageUrlNullable(instance?.faviconUrl, 'preview') ?? getProxiedImageUrlNullable(instance?.iconUrl, 'preview');
}
async function toggleBlock(): Promise<void> {
@ -188,8 +188,9 @@ async function toggleSilenced(): Promise<void> {
if (!meta) throw new Error('No meta?');
if (!instance) throw new Error('No instance?');
const { host } = instance;
const silencedHosts = meta.silencedHosts ?? [];
await os.api('admin/update-meta', {
silencedHosts: isSilenced ? meta.silencedHosts.concat([host]) : meta.silencedHosts.filter(x => x !== host),
silencedHosts: isSilenced ? silencedHosts.concat([host]) : silencedHosts.filter(x => x !== host),
});
}

View file

@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkPagination ref="pagingComponent" :pagination="pagination">
<template #default="{ items }">
<div class="_gaps_s">
<MkInviteCode v-for="item in (items as Misskey.entities.Invite[])" :key="item.id" :invite="item" :onDeleted="deleted"/>
<MkInviteCode v-for="item in (items as Misskey.entities.InviteCode[])" :key="item.id" :invite="item" :onDeleted="deleted"/>
</div>
</template>
</MkPagination>

View file

@ -270,37 +270,37 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts.numberOfPageCache }}</template>
<template #caption>{{ i18n.ts.numberOfPageCacheDescription }}</template>
</MkRange>
</div>
</FormSection>
<FormSection>
<template #label>{{ i18n.ts.dataSaver }} <span class="_beta">CherryPick</span></template>
<MkFolder>
<template #label>{{ i18n.ts.dataSaver }}</template>
<div class="_gaps_m">
<MkInfo>{{ i18n.ts.tryReloadIfNotApplied }}</MkInfo>
<div class="_gaps_m">
<MkInfo>{{ i18n.ts.reloadRequiredToApplySettings }}</MkInfo>
<div class="_buttons">
<MkButton inline @click="enableAllDataSaver">{{ i18n.ts.enableAll }}</MkButton>
<MkButton inline @click="disableAllDataSaver">{{ i18n.ts.disableAll }}</MkButton>
</div>
<div class="_gaps_m">
<MkSwitch v-model="dataSaver.media">
{{ i18n.ts._dataSaver._media.title }}
<template #caption>{{ i18n.ts._dataSaver._media.description }}</template>
</MkSwitch>
<MkSwitch v-model="dataSaver.avatar">
{{ i18n.ts._dataSaver._avatar.title }}
<template #caption>{{ i18n.ts._dataSaver._avatar.description }}</template>
</MkSwitch>
<MkSwitch v-model="dataSaver.urlPreview">
{{ i18n.ts._dataSaver._urlPreview.title }}
<template #caption>{{ i18n.ts._dataSaver._urlPreview.description }}</template>
</MkSwitch>
<MkSwitch v-model="dataSaver.code">
{{ i18n.ts._dataSaver._code.title }}
<template #caption>{{ i18n.ts._dataSaver._code.description }}</template>
</MkSwitch>
</div>
<div class="_buttons">
<MkButton inline @click="enableAllDataSaver">{{ i18n.ts.enableAll }}</MkButton>
<MkButton inline @click="disableAllDataSaver">{{ i18n.ts.disableAll }}</MkButton>
</div>
<div class="_gaps_m">
<MkSwitch v-model="dataSaver.media">
{{ i18n.ts._dataSaver._media.title }}
<template #caption>{{ i18n.ts._dataSaver._media.description }}</template>
</MkSwitch>
<MkSwitch v-model="dataSaver.avatar">
{{ i18n.ts._dataSaver._avatar.title }}
<template #caption>{{ i18n.ts._dataSaver._avatar.description }}</template>
</MkSwitch>
<MkSwitch v-model="dataSaver.urlPreview">
{{ i18n.ts._dataSaver._urlPreview.title }}
<template #caption>{{ i18n.ts._dataSaver._urlPreview.description }}</template>
</MkSwitch>
<MkSwitch v-model="dataSaver.code">
{{ i18n.ts._dataSaver._code.title }}
<template #caption>{{ i18n.ts._dataSaver._code.description }}</template>
</MkSwitch>
</div>
</div>
</MkFolder>
</div>
</FormSection>
@ -576,13 +576,13 @@ function testNotification(): void {
}
function enableAllDataSaver() {
const g = defaultStore.state.dataSaver;
const g = { ...defaultStore.state.dataSaver };
Object.keys(g).forEach((key) => { g[key] = true; });
dataSaver.value = g;
}
function disableAllDataSaver() {
const g = defaultStore.state.dataSaver;
const g = { ...defaultStore.state.dataSaver };
Object.keys(g).forEach((key) => { g[key] = false; });
dataSaver.value = g;
}

View file

@ -254,10 +254,12 @@ const headerActions = $computed(() => {
type: 'switch',
text: i18n.ts.showRepliesToOthersInTimeline,
ref: $$(withReplies),
disabled: $$(onlyFiles),
} : undefined, {
type: 'switch',
text: i18n.ts.fileAttachedOnly,
ref: $$(onlyFiles),
disabled: src === 'local' || src === 'social' ? $$(withReplies) : false,
}, {
type: 'switch',
text: i18n.ts.showCatOnly,

View file

@ -90,7 +90,6 @@ onMounted(() => {
os.api('users/notes', {
userId: props.user.id,
withFiles: true,
excludeNsfw: defaultStore.state.nsfw !== 'ignore',
limit: 15,
}).then(notes => {
for (const note of notes) {

View file

@ -48,10 +48,14 @@ import MkNumber from '@/components/MkNumber.vue';
import MkVisitorDashboard from '@/components/MkVisitorDashboard.vue';
import { getProxiedImageUrl } from '@/scripts/media-proxy.js';
let meta = $ref<Misskey.entities.Instance>();
let instances = $ref<any[]>();
let meta = $ref<Misskey.entities.MetaResponse>();
let instances = $ref<Misskey.entities.FederationInstance[]>();
function getInstanceIcon(instance: Misskey.entities.FederationInstance): string {
if (!instance.iconUrl) {
return '';
}
function getInstanceIcon(instance): string {
return getProxiedImageUrl(instance.iconUrl, 'preview');
}

View file

@ -10,7 +10,12 @@ import { $i } from '@/account.js';
export const pendingApiRequestsCount = ref(0);
// Implements Misskey.api.ApiClient.request
export function api<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(endpoint: E, data: P = {} as any, token?: string | null | undefined, signal?: AbortSignal): Promise<Misskey.Endpoints[E]['res']> {
export function api<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(
endpoint: E,
data: P = {} as any,
token?: string | null | undefined,
signal?: AbortSignal,
): Promise<Misskey.api.SwitchCaseResponseType<E, P>> {
if (endpoint.includes('://')) throw new Error('invalid endpoint');
pendingApiRequestsCount.value++;
@ -51,7 +56,12 @@ export function api<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoin
return promise;
}
export function apiExternal<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(hostUrl: string, endpoint: E, data: P = {} as any, token?: string | null | undefined, signal?: AbortSignal): Promise<Misskey.Endpoints[E]['res']> {
export function apiExternal<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(
hostUrl: string,
endpoint: E, data: P = {} as any,
token?: string | null | undefined,
signal?: AbortSignal,
): Promise<Misskey.api.SwitchCaseResponseType<E, P>> {
if (!/^https?:\/\//.test(hostUrl)) throw new Error('invalid host name');
if (endpoint.includes('://')) throw new Error('invalid endpoint');
pendingApiRequestsCount.value++;
@ -95,7 +105,10 @@ export function apiExternal<E extends keyof Misskey.Endpoints, P extends Misskey
}
// Implements Misskey.api.ApiClient.request
export function apiGet <E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(endpoint: E, data: P = {} as any): Promise<Misskey.Endpoints[E]['res']> {
export function apiGet<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(
endpoint: E,
data: P = {} as any,
): Promise<Misskey.api.SwitchCaseResponseType<E, P>> {
pendingApiRequestsCount.value++;
const onFinally = () => {

View file

@ -0,0 +1,57 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey, cherrypick contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { defineAsyncComponent, Ref, ref } from 'vue';
import { popup } from '@/os.js';
/**
*
* {@link ReactionPicker}
* 稿
* 使
*/
class EmojiPicker {
private src: Ref<HTMLElement | null> = ref(null);
private manualShowing = ref(false);
private onChosen?: (emoji: string) => void;
private onClosed?: () => void;
constructor() {
// nop
}
public async init() {
await popup(defineAsyncComponent(() => import('@/components/MkEmojiPickerDialog.vue')), {
src: this.src,
asReactionPicker: false,
manualShowing: this.manualShowing,
choseAndClose: false,
}, {
done: emoji => {
if (this.onChosen) this.onChosen(emoji);
},
close: () => {
this.manualShowing.value = false;
},
closed: () => {
this.src.value = null;
if (this.onClosed) this.onClosed();
},
});
}
public show(
src: HTMLElement,
onChosen: EmojiPicker['onChosen'],
onClosed: EmojiPicker['onClosed'],
) {
this.src.value = src;
this.manualShowing.value = true;
this.onChosen = onChosen;
this.onClosed = onClosed;
}
}
export const emojiPicker = new EmojiPicker();

View file

@ -60,7 +60,7 @@ export const getBuiltinThemes = () => Promise.all(
'd-cherry',
'd-ice',
'd-u0',
].map(name => import(`../themes/${name}.json5`).then(({ default: _default }): Theme => _default)),
].map(name => import(`@/themes/${name}.json5`).then(({ default: _default }): Theme => _default)),
);
export const getBuiltinThemesRef = () => {

View file

@ -416,10 +416,6 @@ export const defaultStore = markRaw(new Storage('base', {
where: 'device',
default: true,
},
showUnreadNotificationsCount: {
where: 'deviceAccount',
default: false,
},
dataSaver: {
where: 'device',
default: {
@ -429,6 +425,10 @@ export const defaultStore = markRaw(new Storage('base', {
code: false,
} as Record<string, boolean>,
},
showUnreadNotificationsCount: {
where: 'deviceAccount',
default: false,
},
sound_masterVolume: {
where: 'device',

View file

@ -234,6 +234,10 @@ hr {
background: var(--divider);
}
rt {
white-space: initial;
}
.ti {
width: 1.28em;
vertical-align: -12%;

View file

@ -14,7 +14,7 @@ export type MenuLabel = { type: 'label', text: string };
export type MenuLink = { type: 'link', to: string, text: string, icon?: string, indicate?: boolean, avatar?: Misskey.entities.User };
export type MenuA = { type: 'a', href: string, target?: string, download?: string, text: string, icon?: string, indicate?: boolean };
export type MenuUser = { type: 'user', user: Misskey.entities.User, active?: boolean, indicate?: boolean, action: MenuAction };
export type MenuSwitch = { type: 'switch', ref: Ref<boolean>, text: string, disabled?: boolean; action?: MenuAction };
export type MenuSwitch = { type: 'switch', ref: Ref<boolean>, text: string, disabled?: boolean | Ref<boolean>; action?: MenuAction };
export type MenuButton = { type?: 'button', text: string, icon?: string, indicate?: boolean, danger?: boolean, active?: boolean, avatar?: Misskey.entities.User; action: MenuAction };
export type MenuParent = { type: 'parent', text: string, icon?: string, children: MenuItem[] | (() => Promise<MenuItem[]> | MenuItem[]) };

View file

@ -47,7 +47,7 @@ const props = defineProps<{
refreshIntervalSec?: number;
}>();
const instances = ref<Misskey.entities.Instance[]>([]);
const instances = ref<Misskey.entities.FederationInstance[]>([]);
const fetching = ref(true);
let key = $ref(0);

View file

@ -128,10 +128,12 @@ const menu = [{
type: 'switch',
text: i18n.ts.showRepliesToOthersInTimeline,
ref: $$(withReplies),
disabled: $$(onlyFiles),
} : undefined, {
type: 'switch',
text: i18n.ts.fileAttachedOnly,
ref: $$(onlyFiles),
disabled: props.column.tl === 'local' || props.column.tl === 'social' ? $$(withReplies) : false,
}, {
type: 'switch',
text: i18n.ts.showCatOnly,

View file

@ -50,6 +50,9 @@ importers:
eslint:
specifier: 8.54.0
version: 8.54.0
ncp:
specifier: 2.0.0
version: 2.0.0
start-server-and-test:
specifier: 2.0.3
version: 2.0.3
@ -704,6 +707,9 @@ importers:
mock-socket:
specifier: 9.3.1
version: 9.3.1
ncp:
specifier: 2.0.0
version: 2.0.0
tsd:
specifier: 0.29.0
version: 0.29.0
@ -711,6 +717,39 @@ importers:
specifier: 5.3.2
version: 5.3.2
packages/cherrypick-js/generator:
devDependencies:
'@apidevtools/swagger-parser':
specifier: 10.1.0
version: 10.1.0(openapi-types@12.1.3)
'@types/node':
specifier: 20.9.1
version: 20.9.1
'@typescript-eslint/eslint-plugin':
specifier: 6.11.0
version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.2)
'@typescript-eslint/parser':
specifier: 6.11.0
version: 6.11.0(eslint@8.53.0)(typescript@5.3.2)
eslint:
specifier: 8.53.0
version: 8.53.0
openapi-types:
specifier: 12.1.3
version: 12.1.3
openapi-typescript:
specifier: 6.7.1
version: 6.7.1
ts-case-convert:
specifier: 2.0.2
version: 2.0.2
tsx:
specifier: 4.4.0
version: 4.4.0
typescript:
specifier: 5.3.2
version: 5.3.2
packages/frontend:
dependencies:
'@discordapp/twemoji':
@ -1149,6 +1188,38 @@ packages:
'@jridgewell/trace-mapping': 0.3.18
dev: true
/@apidevtools/json-schema-ref-parser@9.0.6:
resolution: {integrity: sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg==}
dependencies:
'@jsdevtools/ono': 7.1.3
call-me-maybe: 1.0.2
js-yaml: 3.14.1
dev: true
/@apidevtools/openapi-schemas@2.1.0:
resolution: {integrity: sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==}
engines: {node: '>=10'}
dev: true
/@apidevtools/swagger-methods@3.0.2:
resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==}
dev: true
/@apidevtools/swagger-parser@10.1.0(openapi-types@12.1.3):
resolution: {integrity: sha512-9Kt7EuS/7WbMAUv2gSziqjvxwDbFSg3Xeyfuj5laUODX8o/k/CpsAKiQ8W7/R88eXFTMbJYg6+7uAmOWNKmwnw==}
peerDependencies:
openapi-types: '>=7'
dependencies:
'@apidevtools/json-schema-ref-parser': 9.0.6
'@apidevtools/openapi-schemas': 2.1.0
'@apidevtools/swagger-methods': 3.0.2
'@jsdevtools/ono': 7.1.3
ajv: 8.12.0
ajv-draft-04: 1.0.0(ajv@8.12.0)
call-me-maybe: 1.0.2
openapi-types: 12.1.3
dev: true
/@aw-web-design/x-default-browser@1.4.126:
resolution: {integrity: sha512-Xk1sIhyNC/esHGGVjL/niHLowM0csl/kFO5uawBy4IrWwy0o1G8LGt3jP6nmWGz+USxeeqbihAmp/oVZju6wug==}
hasBin: true
@ -3356,6 +3427,15 @@ packages:
dev: true
optional: true
/@esbuild/android-arm64@0.18.20:
resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/android-arm64@0.19.8:
resolution: {integrity: sha512-B8JbS61bEunhfx8kasogFENgQfr/dIp+ggYXwTqdbMAgGDhRa3AaPpQMuQU0rNxDLECj6FhDzk1cF9WHMVwrtA==}
engines: {node: '>=12'}
@ -3373,6 +3453,15 @@ packages:
dev: true
optional: true
/@esbuild/android-arm@0.18.20:
resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/android-arm@0.19.8:
resolution: {integrity: sha512-31E2lxlGM1KEfivQl8Yf5aYU/mflz9g06H6S15ITUFQueMFtFjESRMoDSkvMo8thYvLBax+VKTPlpnx+sPicOA==}
engines: {node: '>=12'}
@ -3390,6 +3479,15 @@ packages:
dev: true
optional: true
/@esbuild/android-x64@0.18.20:
resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/android-x64@0.19.8:
resolution: {integrity: sha512-rdqqYfRIn4jWOp+lzQttYMa2Xar3OK9Yt2fhOhzFXqg0rVWEfSclJvZq5fZslnz6ypHvVf3CT7qyf0A5pM682A==}
engines: {node: '>=12'}
@ -3407,6 +3505,15 @@ packages:
dev: true
optional: true
/@esbuild/darwin-arm64@0.18.20:
resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@esbuild/darwin-arm64@0.19.8:
resolution: {integrity: sha512-RQw9DemMbIq35Bprbboyf8SmOr4UXsRVxJ97LgB55VKKeJOOdvsIPy0nFyF2l8U+h4PtBx/1kRf0BelOYCiQcw==}
engines: {node: '>=12'}
@ -3424,6 +3531,15 @@ packages:
dev: true
optional: true
/@esbuild/darwin-x64@0.18.20:
resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/@esbuild/darwin-x64@0.19.8:
resolution: {integrity: sha512-3sur80OT9YdeZwIVgERAysAbwncom7b4bCI2XKLjMfPymTud7e/oY4y+ci1XVp5TfQp/bppn7xLw1n/oSQY3/Q==}
engines: {node: '>=12'}
@ -3441,6 +3557,15 @@ packages:
dev: true
optional: true
/@esbuild/freebsd-arm64@0.18.20:
resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/freebsd-arm64@0.19.8:
resolution: {integrity: sha512-WAnPJSDattvS/XtPCTj1tPoTxERjcTpH6HsMr6ujTT+X6rylVe8ggxk8pVxzf5U1wh5sPODpawNicF5ta/9Tmw==}
engines: {node: '>=12'}
@ -3458,6 +3583,15 @@ packages:
dev: true
optional: true
/@esbuild/freebsd-x64@0.18.20:
resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/freebsd-x64@0.19.8:
resolution: {integrity: sha512-ICvZyOplIjmmhjd6mxi+zxSdpPTKFfyPPQMQTK/w+8eNK6WV01AjIztJALDtwNNfFhfZLux0tZLC+U9nSyA5Zg==}
engines: {node: '>=12'}
@ -3475,6 +3609,15 @@ packages:
dev: true
optional: true
/@esbuild/linux-arm64@0.18.20:
resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-arm64@0.19.8:
resolution: {integrity: sha512-z1zMZivxDLHWnyGOctT9JP70h0beY54xDDDJt4VpTX+iwA77IFsE1vCXWmprajJGa+ZYSqkSbRQ4eyLCpCmiCQ==}
engines: {node: '>=12'}
@ -3492,6 +3635,15 @@ packages:
dev: true
optional: true
/@esbuild/linux-arm@0.18.20:
resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-arm@0.19.8:
resolution: {integrity: sha512-H4vmI5PYqSvosPaTJuEppU9oz1dq2A7Mr2vyg5TF9Ga+3+MGgBdGzcyBP7qK9MrwFQZlvNyJrvz6GuCaj3OukQ==}
engines: {node: '>=12'}
@ -3509,6 +3661,15 @@ packages:
dev: true
optional: true
/@esbuild/linux-ia32@0.18.20:
resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-ia32@0.19.8:
resolution: {integrity: sha512-1a8suQiFJmZz1khm/rDglOc8lavtzEMRo0v6WhPgxkrjcU0LkHj+TwBrALwoz/OtMExvsqbbMI0ChyelKabSvQ==}
engines: {node: '>=12'}
@ -3526,6 +3687,15 @@ packages:
dev: true
optional: true
/@esbuild/linux-loong64@0.18.20:
resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-loong64@0.19.8:
resolution: {integrity: sha512-fHZWS2JJxnXt1uYJsDv9+b60WCc2RlvVAy1F76qOLtXRO+H4mjt3Tr6MJ5l7Q78X8KgCFudnTuiQRBhULUyBKQ==}
engines: {node: '>=12'}
@ -3543,6 +3713,15 @@ packages:
dev: true
optional: true
/@esbuild/linux-mips64el@0.18.20:
resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-mips64el@0.19.8:
resolution: {integrity: sha512-Wy/z0EL5qZYLX66dVnEg9riiwls5IYnziwuju2oUiuxVc+/edvqXa04qNtbrs0Ukatg5HEzqT94Zs7J207dN5Q==}
engines: {node: '>=12'}
@ -3560,6 +3739,15 @@ packages:
dev: true
optional: true
/@esbuild/linux-ppc64@0.18.20:
resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-ppc64@0.19.8:
resolution: {integrity: sha512-ETaW6245wK23YIEufhMQ3HSeHO7NgsLx8gygBVldRHKhOlD1oNeNy/P67mIh1zPn2Hr2HLieQrt6tWrVwuqrxg==}
engines: {node: '>=12'}
@ -3577,6 +3765,15 @@ packages:
dev: true
optional: true
/@esbuild/linux-riscv64@0.18.20:
resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-riscv64@0.19.8:
resolution: {integrity: sha512-T2DRQk55SgoleTP+DtPlMrxi/5r9AeFgkhkZ/B0ap99zmxtxdOixOMI570VjdRCs9pE4Wdkz7JYrsPvsl7eESg==}
engines: {node: '>=12'}
@ -3594,6 +3791,15 @@ packages:
dev: true
optional: true
/@esbuild/linux-s390x@0.18.20:
resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-s390x@0.19.8:
resolution: {integrity: sha512-NPxbdmmo3Bk7mbNeHmcCd7R7fptJaczPYBaELk6NcXxy7HLNyWwCyDJ/Xx+/YcNH7Im5dHdx9gZ5xIwyliQCbg==}
engines: {node: '>=12'}
@ -3611,6 +3817,15 @@ packages:
dev: true
optional: true
/@esbuild/linux-x64@0.18.20:
resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-x64@0.19.8:
resolution: {integrity: sha512-lytMAVOM3b1gPypL2TRmZ5rnXl7+6IIk8uB3eLsV1JwcizuolblXRrc5ShPrO9ls/b+RTp+E6gbsuLWHWi2zGg==}
engines: {node: '>=12'}
@ -3628,6 +3843,15 @@ packages:
dev: true
optional: true
/@esbuild/netbsd-x64@0.18.20:
resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/netbsd-x64@0.19.8:
resolution: {integrity: sha512-hvWVo2VsXz/8NVt1UhLzxwAfo5sioj92uo0bCfLibB0xlOmimU/DeAEsQILlBQvkhrGjamP0/el5HU76HAitGw==}
engines: {node: '>=12'}
@ -3645,6 +3869,15 @@ packages:
dev: true
optional: true
/@esbuild/openbsd-x64@0.18.20:
resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
requiresBuild: true
dev: true
optional: true
/@esbuild/openbsd-x64@0.19.8:
resolution: {integrity: sha512-/7Y7u77rdvmGTxR83PgaSvSBJCC2L3Kb1M/+dmSIvRvQPXXCuC97QAwMugBNG0yGcbEGfFBH7ojPzAOxfGNkwQ==}
engines: {node: '>=12'}
@ -3662,6 +3895,15 @@ packages:
dev: true
optional: true
/@esbuild/sunos-x64@0.18.20:
resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
requiresBuild: true
dev: true
optional: true
/@esbuild/sunos-x64@0.19.8:
resolution: {integrity: sha512-9Lc4s7Oi98GqFA4HzA/W2JHIYfnXbUYgekUP/Sm4BG9sfLjyv6GKKHKKVs83SMicBF2JwAX6A1PuOLMqpD001w==}
engines: {node: '>=12'}
@ -3679,6 +3921,15 @@ packages:
dev: true
optional: true
/@esbuild/win32-arm64@0.18.20:
resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-arm64@0.19.8:
resolution: {integrity: sha512-rq6WzBGjSzihI9deW3fC2Gqiak68+b7qo5/3kmB6Gvbh/NYPA0sJhrnp7wgV4bNwjqM+R2AApXGxMO7ZoGhIJg==}
engines: {node: '>=12'}
@ -3696,6 +3947,15 @@ packages:
dev: true
optional: true
/@esbuild/win32-ia32@0.18.20:
resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-ia32@0.19.8:
resolution: {integrity: sha512-AIAbverbg5jMvJznYiGhrd3sumfwWs8572mIJL5NQjJa06P8KfCPWZQ0NwZbPQnbQi9OWSZhFVSUWjjIrn4hSw==}
engines: {node: '>=12'}
@ -3713,6 +3973,15 @@ packages:
dev: true
optional: true
/@esbuild/win32-x64@0.18.20:
resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/@esbuild/win32-x64@0.19.8:
resolution: {integrity: sha512-bfZ0cQ1uZs2PqpulNL5j/3w+GDhP36k1K5c38QdQg+Swy51jFZWWeIkteNsufkQxp986wnqRRsb/bHbY1WQ7TA==}
engines: {node: '>=12'}
@ -3721,6 +3990,16 @@ packages:
requiresBuild: true
optional: true
/@eslint-community/eslint-utils@4.4.0(eslint@8.53.0):
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
dependencies:
eslint: 8.53.0
eslint-visitor-keys: 3.4.3
dev: true
/@eslint-community/eslint-utils@4.4.0(eslint@8.54.0):
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@ -3753,6 +4032,11 @@ packages:
- supports-color
dev: true
/@eslint/js@8.53.0:
resolution: {integrity: sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
/@eslint/js@8.54.0:
resolution: {integrity: sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@ -3789,6 +4073,11 @@ packages:
text-decoding: 1.0.0
dev: false
/@fastify/busboy@2.1.0:
resolution: {integrity: sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==}
engines: {node: '>=14'}
dev: true
/@fastify/cookie@9.2.0:
resolution: {integrity: sha512-fkg1yjjQRHPFAxSHeLC8CqYuNzvR6Lwlj/KjrzQcGjNBK+K82nW+UfCjfN71g1GkoVoc1GTOgIWkFJpcMfMkHQ==}
dependencies:
@ -4432,6 +4721,10 @@ packages:
'@jridgewell/resolve-uri': 3.1.0
'@jridgewell/sourcemap-codec': 1.4.14
/@jsdevtools/ono@7.1.3:
resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==}
dev: true
/@jsdoc/salty@0.2.5:
resolution: {integrity: sha512-TfRP53RqunNe2HBobVBJ0VLhK1HbfvBYeTC1ahnN64PWvyYyGebmMiPkuwvD9fpw2ZbkoPb8Q7mwy0aR8Z9rvw==}
engines: {node: '>=v12.0.0'}
@ -7207,7 +7500,7 @@ packages:
ts-dedent: 2.2.0
type-fest: 2.19.0
vue: 3.3.9(typescript@5.3.2)
vue-component-type-helpers: 1.8.22
vue-component-type-helpers: 1.8.24
transitivePeerDependencies:
- encoding
- supports-color
@ -8111,6 +8404,12 @@ packages:
dependencies:
undici-types: 5.26.5
/@types/node@20.9.1:
resolution: {integrity: sha512-HhmzZh5LSJNS5O8jQKpJ/3ZcrrlG6L70hpGqMIAoM9YVD0YBRNWYsfwcXq8VnSjlNpCpgLzMXdiPo+dxcvSmiA==}
dependencies:
undici-types: 5.26.5
dev: true
/@types/nodemailer@6.4.14:
resolution: {integrity: sha512-fUWthHO9k9DSdPCSPRqcu6TWhYyxTBg382vlNIttSe9M7XfsT06y0f24KHXtbnijPGGRIcVvdKHTNikOI6qiHA==}
dependencies:
@ -8381,6 +8680,35 @@ packages:
dev: true
optional: true
/@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.2):
resolution: {integrity: sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
'@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha
eslint: ^7.0.0 || ^8.0.0
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@eslint-community/regexpp': 4.6.2
'@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
'@typescript-eslint/scope-manager': 6.11.0
'@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
'@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
'@typescript-eslint/visitor-keys': 6.11.0
debug: 4.3.4(supports-color@8.1.1)
eslint: 8.53.0
graphemer: 1.4.0
ignore: 5.2.4
natural-compare: 1.4.0
semver: 7.5.4
ts-api-utils: 1.0.1(typescript@5.3.2)
typescript: 5.3.2
transitivePeerDependencies:
- supports-color
dev: true
/@typescript-eslint/eslint-plugin@6.12.0(@typescript-eslint/parser@6.12.0)(eslint@8.54.0)(typescript@5.3.2):
resolution: {integrity: sha512-XOpZ3IyJUIV1b15M7HVOpgQxPPF7lGXgsfcEIu3yDxFPaf/xZKt7s9QO/pbk7vpWQyVulpJbu4E5LwpZiQo4kA==}
engines: {node: ^16.0.0 || >=18.0.0}
@ -8410,6 +8738,27 @@ packages:
- supports-color
dev: true
/@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.2):
resolution: {integrity: sha512-+whEdjk+d5do5nxfxx73oanLL9ghKO3EwM9kBCkUtWMRwWuPaFv9ScuqlYfQ6pAD6ZiJhky7TZ2ZYhrMsfMxVQ==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
eslint: ^7.0.0 || ^8.0.0
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@typescript-eslint/scope-manager': 6.11.0
'@typescript-eslint/types': 6.11.0
'@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.2)
'@typescript-eslint/visitor-keys': 6.11.0
debug: 4.3.4(supports-color@8.1.1)
eslint: 8.53.0
typescript: 5.3.2
transitivePeerDependencies:
- supports-color
dev: true
/@typescript-eslint/parser@6.12.0(eslint@8.54.0)(typescript@5.3.2):
resolution: {integrity: sha512-s8/jNFPKPNRmXEnNXfuo1gemBdVmpQsK1pcu+QIvuNJuhFzGrpD7WjOcvDc/+uEdfzSYpNu7U/+MmbScjoQ6vg==}
engines: {node: ^16.0.0 || >=18.0.0}
@ -8439,6 +8788,14 @@ packages:
'@typescript-eslint/visitor-keys': 5.62.0
dev: true
/@typescript-eslint/scope-manager@6.11.0:
resolution: {integrity: sha512-0A8KoVvIURG4uhxAdjSaxy8RdRE//HztaZdG8KiHLP8WOXSk0vlF7Pvogv+vlJA5Rnjj/wDcFENvDaHb+gKd1A==}
engines: {node: ^16.0.0 || >=18.0.0}
dependencies:
'@typescript-eslint/types': 6.11.0
'@typescript-eslint/visitor-keys': 6.11.0
dev: true
/@typescript-eslint/scope-manager@6.12.0:
resolution: {integrity: sha512-5gUvjg+XdSj8pcetdL9eXJzQNTl3RD7LgUiYTl8Aabdi8hFkaGSYnaS6BLc0BGNaDH+tVzVwmKtWvu0jLgWVbw==}
engines: {node: ^16.0.0 || >=18.0.0}
@ -8447,6 +8804,26 @@ packages:
'@typescript-eslint/visitor-keys': 6.12.0
dev: true
/@typescript-eslint/type-utils@6.11.0(eslint@8.53.0)(typescript@5.3.2):
resolution: {integrity: sha512-nA4IOXwZtqBjIoYrJcYxLRO+F9ri+leVGoJcMW1uqr4r1Hq7vW5cyWrA43lFbpRvQ9XgNrnfLpIkO3i1emDBIA==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
eslint: ^7.0.0 || ^8.0.0
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.2)
'@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
debug: 4.3.4(supports-color@8.1.1)
eslint: 8.53.0
ts-api-utils: 1.0.1(typescript@5.3.2)
typescript: 5.3.2
transitivePeerDependencies:
- supports-color
dev: true
/@typescript-eslint/type-utils@6.12.0(eslint@8.54.0)(typescript@5.3.2):
resolution: {integrity: sha512-WWmRXxhm1X8Wlquj+MhsAG4dU/Blvf1xDgGaYCzfvStP2NwPQh6KBvCDbiOEvaE0filhranjIlK/2fSTVwtBng==}
engines: {node: ^16.0.0 || >=18.0.0}
@ -8472,6 +8849,11 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
/@typescript-eslint/types@6.11.0:
resolution: {integrity: sha512-ZbEzuD4DwEJxwPqhv3QULlRj8KYTAnNsXxmfuUXFCxZmO6CF2gM/y+ugBSAQhrqaJL3M+oe4owdWunaHM6beqA==}
engines: {node: ^16.0.0 || >=18.0.0}
dev: true
/@typescript-eslint/types@6.12.0:
resolution: {integrity: sha512-MA16p/+WxM5JG/F3RTpRIcuOghWO30//VEOvzubM8zuOOBYXsP+IfjoCXXiIfy2Ta8FRh9+IO9QLlaFQUU+10Q==}
engines: {node: ^16.0.0 || >=18.0.0}
@ -8498,6 +8880,27 @@ packages:
- supports-color
dev: true
/@typescript-eslint/typescript-estree@6.11.0(typescript@5.3.2):
resolution: {integrity: sha512-Aezzv1o2tWJwvZhedzvD5Yv7+Lpu1by/U1LZ5gLc4tCx8jUmuSCMioPFRjliN/6SJIvY6HpTtJIWubKuYYYesQ==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
'@typescript-eslint/types': 6.11.0
'@typescript-eslint/visitor-keys': 6.11.0
debug: 4.3.4(supports-color@8.1.1)
globby: 11.1.0
is-glob: 4.0.3
semver: 7.5.4
ts-api-utils: 1.0.1(typescript@5.3.2)
typescript: 5.3.2
transitivePeerDependencies:
- supports-color
dev: true
/@typescript-eslint/typescript-estree@6.12.0(typescript@5.3.2):
resolution: {integrity: sha512-vw9E2P9+3UUWzhgjyyVczLWxZ3GuQNT7QpnIY3o5OMeLO/c8oHljGc8ZpryBMIyympiAAaKgw9e5Hl9dCWFOYw==}
engines: {node: ^16.0.0 || >=18.0.0}
@ -8539,6 +8942,25 @@ packages:
- typescript
dev: true
/@typescript-eslint/utils@6.11.0(eslint@8.53.0)(typescript@5.3.2):
resolution: {integrity: sha512-p23ibf68fxoZy605dc0dQAEoUsoiNoP3MD9WQGiHLDuTSOuqoTsa4oAy+h3KDkTcxbbfOtUjb9h3Ta0gT4ug2g==}
engines: {node: ^16.0.0 || >=18.0.0}
peerDependencies:
eslint: ^7.0.0 || ^8.0.0
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0)
'@types/json-schema': 7.0.12
'@types/semver': 7.5.6
'@typescript-eslint/scope-manager': 6.11.0
'@typescript-eslint/types': 6.11.0
'@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.2)
eslint: 8.53.0
semver: 7.5.4
transitivePeerDependencies:
- supports-color
- typescript
dev: true
/@typescript-eslint/utils@6.12.0(eslint@8.54.0)(typescript@5.3.2):
resolution: {integrity: sha512-LywPm8h3tGEbgfyjYnu3dauZ0U7R60m+miXgKcZS8c7QALO9uWJdvNoP+duKTk2XMWc7/Q3d/QiCuLN9X6SWyQ==}
engines: {node: ^16.0.0 || >=18.0.0}
@ -8566,6 +8988,14 @@ packages:
eslint-visitor-keys: 3.4.3
dev: true
/@typescript-eslint/visitor-keys@6.11.0:
resolution: {integrity: sha512-+SUN/W7WjBr05uRxPggJPSzyB8zUpaYo2hByKasWbqr3PM8AXfZt8UHdNpBS1v9SA62qnSSMF3380SwDqqprgQ==}
engines: {node: ^16.0.0 || >=18.0.0}
dependencies:
'@typescript-eslint/types': 6.11.0
eslint-visitor-keys: 3.4.3
dev: true
/@typescript-eslint/visitor-keys@6.12.0:
resolution: {integrity: sha512-rg3BizTZHF1k3ipn8gfrzDXXSFKyOEB5zxYXInQ6z0hUvmQlhaZQzK+YmHmNViMA9HzW5Q9+bPPt90bU6GQwyw==}
engines: {node: ^16.0.0 || >=18.0.0}
@ -9027,6 +9457,17 @@ packages:
clean-stack: 2.2.0
indent-string: 4.0.0
/ajv-draft-04@1.0.0(ajv@8.12.0):
resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==}
peerDependencies:
ajv: ^8.5.0
peerDependenciesMeta:
ajv:
optional: true
dependencies:
ajv: 8.12.0
dev: true
/ajv-formats@2.1.1(ajv@8.12.0):
resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
peerDependencies:
@ -9053,7 +9494,6 @@ packages:
json-schema-traverse: 1.0.0
require-from-string: 2.0.2
uri-js: 4.4.1
dev: false
/ansi-colors@4.1.3:
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
@ -9895,6 +10335,10 @@ packages:
function-bind: 1.1.1
get-intrinsic: 1.2.0
/call-me-maybe@1.0.2:
resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==}
dev: true
/callsites@3.1.0:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'}
@ -11453,6 +11897,36 @@ packages:
'@esbuild/win32-x64': 0.18.17
dev: true
/esbuild@0.18.20:
resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
optionalDependencies:
'@esbuild/android-arm': 0.18.20
'@esbuild/android-arm64': 0.18.20
'@esbuild/android-x64': 0.18.20
'@esbuild/darwin-arm64': 0.18.20
'@esbuild/darwin-x64': 0.18.20
'@esbuild/freebsd-arm64': 0.18.20
'@esbuild/freebsd-x64': 0.18.20
'@esbuild/linux-arm': 0.18.20
'@esbuild/linux-arm64': 0.18.20
'@esbuild/linux-ia32': 0.18.20
'@esbuild/linux-loong64': 0.18.20
'@esbuild/linux-mips64el': 0.18.20
'@esbuild/linux-ppc64': 0.18.20
'@esbuild/linux-riscv64': 0.18.20
'@esbuild/linux-s390x': 0.18.20
'@esbuild/linux-x64': 0.18.20
'@esbuild/netbsd-x64': 0.18.20
'@esbuild/openbsd-x64': 0.18.20
'@esbuild/sunos-x64': 0.18.20
'@esbuild/win32-arm64': 0.18.20
'@esbuild/win32-ia32': 0.18.20
'@esbuild/win32-x64': 0.18.20
dev: true
/esbuild@0.19.8:
resolution: {integrity: sha512-l7iffQpT2OrZfH2rXIp7/FkmaeZM0vxbxN9KfiCwGYuZqzMg/JdvX26R31Zxn/Pxvsrg3Y9N6XTcnknqDyyv4w==}
engines: {node: '>=12'}
@ -11681,6 +12155,53 @@ packages:
resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
/eslint@8.53.0:
resolution: {integrity: sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
hasBin: true
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0)
'@eslint-community/regexpp': 4.6.2
'@eslint/eslintrc': 2.1.3
'@eslint/js': 8.53.0
'@humanwhocodes/config-array': 0.11.13
'@humanwhocodes/module-importer': 1.0.1
'@nodelib/fs.walk': 1.2.8
'@ungap/structured-clone': 1.2.0
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.3
debug: 4.3.4(supports-color@8.1.1)
doctrine: 3.0.0
escape-string-regexp: 4.0.0
eslint-scope: 7.2.2
eslint-visitor-keys: 3.4.3
espree: 9.6.1
esquery: 1.4.2
esutils: 2.0.3
fast-deep-equal: 3.1.3
file-entry-cache: 6.0.1
find-up: 5.0.0
glob-parent: 6.0.2
globals: 13.19.0
graphemer: 1.4.0
ignore: 5.2.4
imurmurhash: 0.1.4
is-glob: 4.0.3
is-path-inside: 3.0.3
js-yaml: 4.1.0
json-stable-stringify-without-jsonify: 1.0.1
levn: 0.4.1
lodash.merge: 4.6.2
minimatch: 3.1.2
natural-compare: 1.4.0
optionator: 0.9.3
strip-ansi: 6.0.1
text-table: 0.2.0
transitivePeerDependencies:
- supports-color
dev: true
/eslint@8.54.0:
resolution: {integrity: sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@ -12643,6 +13164,12 @@ packages:
get-intrinsic: 1.2.1
dev: true
/get-tsconfig@4.7.2:
resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==}
dependencies:
resolve-pkg-maps: 1.0.0
dev: true
/getos@3.2.1:
resolution: {integrity: sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==}
dependencies:
@ -14581,7 +15108,6 @@ packages:
/json-schema-traverse@1.0.0:
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
dev: false
/json-schema@0.4.0:
resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
@ -15570,6 +16096,11 @@ packages:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
dev: true
/ncp@2.0.0:
resolution: {integrity: sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==}
hasBin: true
dev: true
/ndarray-ops@1.2.2:
resolution: {integrity: sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==}
dependencies:
@ -16050,7 +16581,18 @@ packages:
/openapi-types@12.1.3:
resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
dev: false
/openapi-typescript@6.7.1:
resolution: {integrity: sha512-Q3Ltt0KUm2smcPrsaR8qKmSwQ1KM4yGDJVoQdpYa0yvKPeN8huDx5utMT7DvwvJastHHzUxajjivK3WN2+fobg==}
hasBin: true
dependencies:
ansi-colors: 4.1.3
fast-glob: 3.3.2
js-yaml: 4.1.0
supports-color: 9.4.0
undici: 5.28.2
yargs-parser: 21.1.1
dev: true
/opentype.js@0.4.11:
resolution: {integrity: sha512-GthxucX/6aftfLdeU5Ho7o7zmQcC8uVtqdcelVq12X++ndxwBZG8Xb5rFEKT7nEcWDD2P1x+TNuJ70jtj1Mbpw==}
@ -17922,7 +18464,6 @@ packages:
/require-from-string@2.0.2:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
dev: false
/require-main-filename@2.0.0:
resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
@ -17962,6 +18503,10 @@ packages:
engines: {node: '>=8'}
dev: true
/resolve-pkg-maps@1.0.0:
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
dev: true
/resolve.exports@2.0.0:
resolution: {integrity: sha512-6K/gDlqgQscOlg9fSRpWstA8sYe8rbELsSTNpx+3kTrsVCzvSl0zIvRErM7fdl9ERWDsKnrLnwB+Ne89918XOg==}
engines: {node: '>=10'}
@ -19018,6 +19563,11 @@ packages:
dependencies:
has-flag: 4.0.0
/supports-color@9.4.0:
resolution: {integrity: sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==}
engines: {node: '>=12'}
dev: true
/supports-hyperlinks@2.3.0:
resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==}
engines: {node: '>=8'}
@ -19388,6 +19938,10 @@ packages:
typescript: 5.3.2
dev: true
/ts-case-convert@2.0.2:
resolution: {integrity: sha512-vdKfx1VAdpvEBOBv5OpVu5ZFqRg9HdTI4sYt6qqMeICBeNyXvitrarCnFWNDAki51IKwCyx+ZssY46Q9jH5otA==}
dev: true
/ts-dedent@2.2.0:
resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==}
engines: {node: '>=6.10'}
@ -19467,6 +20021,17 @@ packages:
typescript: 5.3.2
dev: true
/tsx@4.4.0:
resolution: {integrity: sha512-4fwcEjRUxW20ciSaMB8zkpGwCPxuRGnadDuj/pBk5S9uT29zvWz15PK36GrKJo45mSJomDxVejZ73c6lr3811Q==}
engines: {node: '>=18.0.0'}
hasBin: true
dependencies:
esbuild: 0.18.20
get-tsconfig: 4.7.2
optionalDependencies:
fsevents: 2.3.3
dev: true
/tunnel-agent@0.6.0:
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
dependencies:
@ -19730,6 +20295,13 @@ packages:
busboy: 1.6.0
dev: false
/undici@5.28.2:
resolution: {integrity: sha512-wh1pHJHnUeQV5Xa8/kyQhO7WFa8M34l026L5P/+2TYiakvGy5Rdc8jWZVyG7ieht/0WgJLEd3kcU5gKx+6GC8w==}
engines: {node: '>=14.0'}
dependencies:
'@fastify/busboy': 2.1.0
dev: true
/unicode-canonical-property-names-ecmascript@2.0.0:
resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==}
engines: {node: '>=4'}
@ -20155,8 +20727,8 @@ packages:
resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==}
dev: false
/vue-component-type-helpers@1.8.22:
resolution: {integrity: sha512-LK3wJHs3vJxHG292C8cnsRusgyC5SEZDCzDCD01mdE/AoREFMl2tzLRuzwyuEsOIz13tqgBcnvysN3Lxsa14Fw==}
/vue-component-type-helpers@1.8.24:
resolution: {integrity: sha512-lqWs/7fdRXoSBAlbouHBX+LNuaY6gI9xWW34m/ZIz9zVPYHEyw0b2/zaCBwlKx0NtKTeF/6pOpvrxVkh7nhIYg==}
dev: true
/vue-component-type-helpers@1.8.4:

View file

@ -3,3 +3,4 @@ packages:
- 'packages/frontend'
- 'packages/sw'
- 'packages/cherrypick-js'
- 'packages/cherrypick-js/generator'