From 3d8eda14a299dbf97cd135f7087c1797bd1ebb37 Mon Sep 17 00:00:00 2001
From: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>
Date: Thu, 25 Jul 2024 16:40:14 +0900
Subject: [PATCH] =?UTF-8?q?[Re]=20refactor(misskey-js):=20=E8=AD=A6?=
 =?UTF-8?q?=E5=91=8A=E3=82=92=E3=81=99=E3=81=B9=E3=81=A6=E8=A7=A3=E6=B1=BA?=
 =?UTF-8?q?=20(#14277)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* chore(misskey-js): Unchanged files with check annotationsで紛らわしい部分の警告を抑制 ロジック面は後で直す

* dummy change to see if the feature do not report them (to be reverted after the check)

* refactor: 型合わせ

* refactor: fix warnings from c22dd6358ba4e068c49be033a07d9fbb001f2347

* lint

* 型合わせ

* キャスト

* pnpm build-misskey-js-with-types

* Revert "dummy change to see if the feature do not report them (to be reverted after the check)"

This reverts commit 67072e3ca6e3e16342ca3b35feadcb41afcbe04f.

* eliminate reversiGame any

* move reversiGame types

* lint

* Update packages/misskey-js/src/streaming.ts

Co-authored-by: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>

* Update acct.ts

* run api extractor

* re-run api extractor

---------

Co-authored-by: Kisaragi Marine <kisaragi.effective@gmail.com>
Co-authored-by: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com>
---
 packages/misskey-js/eslint.config.js       |   1 +
 packages/misskey-js/etc/misskey-js.api.md  |  27 +++---
 packages/misskey-js/src/acct.ts            |   3 +-
 packages/misskey-js/src/api.ts             |   3 +
 packages/misskey-js/src/api.types.ts       |   2 +
 packages/misskey-js/src/consts.ts          | 104 +++++++++++++--------
 packages/misskey-js/src/entities.ts        |   3 +-
 packages/misskey-js/src/streaming.ts       |  30 +++---
 packages/misskey-js/src/streaming.types.ts |  18 ++--
 9 files changed, 118 insertions(+), 73 deletions(-)

diff --git a/packages/misskey-js/eslint.config.js b/packages/misskey-js/eslint.config.js
index e34e7510b2..d8173f30e9 100644
--- a/packages/misskey-js/eslint.config.js
+++ b/packages/misskey-js/eslint.config.js
@@ -1,6 +1,7 @@
 import tsParser from '@typescript-eslint/parser';
 import sharedConfig from '../shared/eslint.config.js';
 
+// eslint-disable-next-line import/no-default-export
 export default [
 	...sharedConfig,
 	{
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index d11d2a4f06..377dd6f658 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -551,7 +551,7 @@ type Channel = components['schemas']['Channel'];
 // Warning: (ae-forgotten-export) The symbol "AnyOf" needs to be exported by the entry point index.d.ts
 //
 // @public (undocumented)
-export abstract class ChannelConnection<Channel extends AnyOf<Channels> = any> extends EventEmitter<Channel['events']> {
+export abstract class ChannelConnection<Channel extends AnyOf<Channels> = AnyOf<Channels>> extends EventEmitter<Channel['events']> {
     constructor(stream: Stream, channel: string, name?: string);
     // (undocumented)
     channel: string;
@@ -771,12 +771,12 @@ export type Channels = {
                 user1: boolean;
                 user2: boolean;
             }) => void;
-            updateSettings: (payload: {
+            updateSettings: <K extends ReversiUpdateKey>(payload: {
                 userId: User['id'];
-                key: string;
-                value: any;
+                key: K;
+                value: ReversiGameDetailed[K];
             }) => void;
-            log: (payload: Record<string, any>) => void;
+            log: (payload: Record<string, unknown>) => void;
         };
         receives: {
             putStone: {
@@ -785,10 +785,7 @@ export type Channels = {
             };
             ready: boolean;
             cancel: null | Record<string, never>;
-            updateSettings: {
-                key: string;
-                value: any;
-            };
+            updateSettings: ReversiUpdateSettings<ReversiUpdateKey>;
             claimTimeIsUp: null | Record<string, never>;
         };
     };
@@ -2730,7 +2727,7 @@ type PagesUnlikeRequest = operations['pages___unlike']['requestBody']['content']
 type PagesUpdateRequest = operations['pages___update']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-function parse(acct: string): Acct;
+function parse(_acct: string): Acct;
 
 // Warning: (ae-forgotten-export) The symbol "Values" needs to be exported by the entry point index.d.ts
 //
@@ -2969,7 +2966,7 @@ export class Stream extends EventEmitter<StreamEvents> {
     constructor(origin: string, user: {
         token: string;
     } | null, options?: {
-        WebSocket?: any;
+        WebSocket?: WebSocket;
     });
     // (undocumented)
     close(): void;
@@ -2992,9 +2989,9 @@ export class Stream extends EventEmitter<StreamEvents> {
     // (undocumented)
     send(typeOrPayload: string): void;
     // (undocumented)
-    send(typeOrPayload: string, payload: any): void;
+    send(typeOrPayload: string, payload: unknown): void;
     // (undocumented)
-    send(typeOrPayload: Record<string, any> | any[]): void;
+    send(typeOrPayload: Record<string, unknown> | unknown[]): void;
     // (undocumented)
     state: 'initializing' | 'reconnecting' | 'connected';
     // (undocumented)
@@ -3229,7 +3226,9 @@ type UsersUpdateMemoRequest = operations['users___update-memo']['requestBody']['
 
 // Warnings were encountered during analysis:
 //
-// src/entities.ts:34:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts
+// src/entities.ts:35:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts
+// src/streaming.types.ts:220:4 - (ae-forgotten-export) The symbol "ReversiUpdateKey" needs to be exported by the entry point index.d.ts
+// src/streaming.types.ts:230:4 - (ae-forgotten-export) The symbol "ReversiUpdateSettings" needs to be exported by the entry point index.d.ts
 
 // (No @packageDocumentation comment for this package)
 
diff --git a/packages/misskey-js/src/acct.ts b/packages/misskey-js/src/acct.ts
index b25bc564ea..aa8658cdbd 100644
--- a/packages/misskey-js/src/acct.ts
+++ b/packages/misskey-js/src/acct.ts
@@ -3,7 +3,8 @@ export type Acct = {
 	host: string | null;
 };
 
-export function parse(acct: string): Acct {
+export function parse(_acct: string): Acct {
+	let acct = _acct;
 	if (acct.startsWith('@')) acct = acct.substring(1);
 	const split = acct.split('@', 2);
 	return { username: split[0], host: split[1] || null };
diff --git a/packages/misskey-js/src/api.ts b/packages/misskey-js/src/api.ts
index 76d055cbe4..ea1df57f3d 100644
--- a/packages/misskey-js/src/api.ts
+++ b/packages/misskey-js/src/api.ts
@@ -14,6 +14,7 @@ export type APIError = {
 	code: string;
 	message: string;
 	kind: 'client' | 'server';
+	// eslint-disable-next-line @typescript-eslint/no-explicit-any
 	info: Record<string, any>;
 };
 
@@ -29,6 +30,7 @@ export type FetchLike = (input: string, init?: {
 	headers: { [key in string]: string }
 }) => Promise<{
 	status: number;
+	// eslint-disable-next-line @typescript-eslint/no-explicit-any
 	json(): Promise<any>;
 }>;
 
@@ -49,6 +51,7 @@ export class APIClient {
 		this.fetch = opts.fetch ?? ((...args) => fetch(...args));
 	}
 
+	// eslint-disable-next-line @typescript-eslint/no-explicit-any
 	private assertIsRecord<T>(obj: T): obj is T & Record<string, any> {
 		return obj !== null && typeof obj === 'object' && !Array.isArray(obj);
 	}
diff --git a/packages/misskey-js/src/api.types.ts b/packages/misskey-js/src/api.types.ts
index 8c403639b7..5ee4194db2 100644
--- a/packages/misskey-js/src/api.types.ts
+++ b/packages/misskey-js/src/api.types.ts
@@ -28,11 +28,13 @@ 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
+		// eslint-disable-next-line @typescript-eslint/no-explicit-any
 		? 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
+		// eslint-disable-next-line @typescript-eslint/no-explicit-any
 		? StrictExtract<Endpoints[E]['res']['$switch']['$cases'][C], [P, any]>[1]
 		: never
 
diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts
index 03b9069290..b509d3280c 100644
--- a/packages/misskey-js/src/consts.ts
+++ b/packages/misskey-js/src/consts.ts
@@ -1,3 +1,13 @@
+import type { operations } from './autogen/types.js';
+import type {
+	AbuseReportNotificationRecipient, Ad,
+	Announcement,
+	EmojiDetailed, InviteCode,
+	MetaDetailed,
+	Note,
+	Role, SystemWebhook, UserLite,
+} from './autogen/models.js';
+
 export const notificationTypes = ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app', 'roleAssigned', 'achievementEarned'] as const;
 
 export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const;
@@ -135,10 +145,30 @@ export const moderationLogTypes = [
 	'unsetUserBanner',
 ] as const;
 
+// See: packages/backend/src/core/ReversiService.ts@L410
+export const reversiUpdateKeys = [
+	'map',
+	'bw',
+	'isLlotheo',
+	'canPutEverywhere',
+	'loopedBoard',
+	'timeLimitForEachTurn',
+] as const;
+
+export type ReversiUpdateKey = typeof reversiUpdateKeys[number];
+
+type AvatarDecoration = UserLite['avatarDecorations'][number];
+
+type ReceivedAbuseReport = {
+	reportId: AbuseReportNotificationRecipient['id'];
+	report: operations['admin___abuse-user-reports']['responses'][200]['content']['application/json'];
+	forwarded: boolean;
+};
+
 export type ModerationLogPayloads = {
 	updateServerSettings: {
-		before: any | null;
-		after: any | null;
+		before: MetaDetailed | null;
+		after: MetaDetailed | null;
 	};
 	suspend: {
 		userId: string;
@@ -159,16 +189,16 @@ export type ModerationLogPayloads = {
 	};
 	addCustomEmoji: {
 		emojiId: string;
-		emoji: any;
+		emoji: EmojiDetailed;
 	};
 	updateCustomEmoji: {
 		emojiId: string;
-		before: any;
-		after: any;
+		before: EmojiDetailed;
+		after: EmojiDetailed;
 	};
 	deleteCustomEmoji: {
 		emojiId: string;
-		emoji: any;
+		emoji: EmojiDetailed;
 	};
 	assignRole: {
 		userId: string;
@@ -187,16 +217,16 @@ export type ModerationLogPayloads = {
 	};
 	createRole: {
 		roleId: string;
-		role: any;
+		role: Role;
 	};
 	updateRole: {
 		roleId: string;
-		before: any;
-		after: any;
+		before: Role;
+		after: Role;
 	};
 	deleteRole: {
 		roleId: string;
-		role: any;
+		role: Role;
 	};
 	clearQueue: Record<string, never>;
 	promoteQueue: Record<string, never>;
@@ -211,39 +241,39 @@ export type ModerationLogPayloads = {
 		noteUserId: string;
 		noteUserUsername: string;
 		noteUserHost: string | null;
-		note: any;
+		note: Note;
 	};
 	createGlobalAnnouncement: {
 		announcementId: string;
-		announcement: any;
+		announcement: Announcement;
 	};
 	createUserAnnouncement: {
 		announcementId: string;
-		announcement: any;
+		announcement: Announcement;
 		userId: string;
 		userUsername: string;
 		userHost: string | null;
 	};
 	updateGlobalAnnouncement: {
 		announcementId: string;
-		before: any;
-		after: any;
+		before: Announcement;
+		after: Announcement;
 	};
 	updateUserAnnouncement: {
 		announcementId: string;
-		before: any;
-		after: any;
+		before: Announcement;
+		after: Announcement;
 		userId: string;
 		userUsername: string;
 		userHost: string | null;
 	};
 	deleteGlobalAnnouncement: {
 		announcementId: string;
-		announcement: any;
+		announcement: Announcement;
 	};
 	deleteUserAnnouncement: {
 		announcementId: string;
-		announcement: any;
+		announcement: Announcement;
 		userId: string;
 		userUsername: string;
 		userHost: string | null;
@@ -281,37 +311,37 @@ export type ModerationLogPayloads = {
 	};
 	resolveAbuseReport: {
 		reportId: string;
-		report: any;
+		report: ReceivedAbuseReport;
 		forwarded: boolean;
 	};
 	createInvitation: {
-		invitations: any[];
+		invitations: InviteCode[];
 	};
 	createAd: {
 		adId: string;
-		ad: any;
+		ad: Ad;
 	};
 	updateAd: {
 		adId: string;
-		before: any;
-		after: any;
+		before: Ad;
+		after: Ad;
 	};
 	deleteAd: {
 		adId: string;
-		ad: any;
+		ad: Ad;
 	};
 	createAvatarDecoration: {
 		avatarDecorationId: string;
-		avatarDecoration: any;
+		avatarDecoration: AvatarDecoration;
 	};
 	updateAvatarDecoration: {
 		avatarDecorationId: string;
-		before: any;
-		after: any;
+		before: AvatarDecoration;
+		after: AvatarDecoration;
 	};
 	deleteAvatarDecoration: {
 		avatarDecorationId: string;
-		avatarDecoration: any;
+		avatarDecoration: AvatarDecoration;
 	};
 	unsetUserAvatar: {
 		userId: string;
@@ -327,28 +357,28 @@ export type ModerationLogPayloads = {
 	};
 	createSystemWebhook: {
 		systemWebhookId: string;
-		webhook: any;
+		webhook: SystemWebhook;
 	};
 	updateSystemWebhook: {
 		systemWebhookId: string;
-		before: any;
-		after: any;
+		before: SystemWebhook;
+		after: SystemWebhook;
 	};
 	deleteSystemWebhook: {
 		systemWebhookId: string;
-		webhook: any;
+		webhook: SystemWebhook;
 	};
 	createAbuseReportNotificationRecipient: {
 		recipientId: string;
-		recipient: any;
+		recipient: AbuseReportNotificationRecipient;
 	};
 	updateAbuseReportNotificationRecipient: {
 		recipientId: string;
-		before: any;
-		after: any;
+		before: AbuseReportNotificationRecipient;
+		after: AbuseReportNotificationRecipient;
 	};
 	deleteAbuseReportNotificationRecipient: {
 		recipientId: string;
-		recipient: any;
+		recipient: AbuseReportNotificationRecipient;
 	};
 };
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index 7331a55a1c..ce58fb2970 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -7,7 +7,7 @@ import {
 	Role,
 	RolePolicies,
 	User,
-	UserDetailedNotMe
+	UserDetailedNotMe,
 } from './autogen/models.js';
 
 export * from './autogen/entities.js';
@@ -19,6 +19,7 @@ export type DateString = string;
 export type PageEvent = {
 	pageId: Page['id'];
 	event: string;
+	// eslint-disable-next-line @typescript-eslint/no-explicit-any
 	var: any;
 	userId: User['id'];
 	user: User;
diff --git a/packages/misskey-js/src/streaming.ts b/packages/misskey-js/src/streaming.ts
index 83930e621c..d1d131cfc1 100644
--- a/packages/misskey-js/src/streaming.ts
+++ b/packages/misskey-js/src/streaming.ts
@@ -15,7 +15,7 @@ export function urlQuery(obj: Record<string, string | number | boolean | undefin
 		.join('&');
 }
 
-type AnyOf<T extends Record<any, any>> = T[keyof T];
+type AnyOf<T extends Record<PropertyKey, unknown>> = T[keyof T];
 
 type StreamEvents = {
 	_connected_: void;
@@ -25,6 +25,7 @@ type StreamEvents = {
 /**
  * Misskey stream connection
  */
+// eslint-disable-next-line import/no-default-export
 export default class Stream extends EventEmitter<StreamEvents> {
 	private stream: _ReconnectingWebsocket.default;
 	public state: 'initializing' | 'reconnecting' | 'connected' = 'initializing';
@@ -34,7 +35,7 @@ export default class Stream extends EventEmitter<StreamEvents> {
 	private idCounter = 0;
 
 	constructor(origin: string, user: { token: string; } | null, options?: {
-		WebSocket?: any;
+		WebSocket?: WebSocket;
 	}) {
 		super();
 
@@ -51,6 +52,7 @@ export default class Stream extends EventEmitter<StreamEvents> {
 		this.send = this.send.bind(this);
 		this.close = this.close.bind(this);
 
+		// eslint-disable-next-line no-param-reassign
 		options = options ?? { };
 
 		const query = urlQuery({
@@ -91,8 +93,8 @@ export default class Stream extends EventEmitter<StreamEvents> {
 			this.sharedConnectionPools.push(pool);
 		}
 
-		const connection = new SharedConnection(this, channel, pool, name);
-		this.sharedConnections.push(connection);
+		const connection = new SharedConnection<Channels[C]>(this, channel, pool, name);
+		this.sharedConnections.push(connection as unknown as SharedConnection);
 		return connection;
 	}
 
@@ -106,7 +108,7 @@ export default class Stream extends EventEmitter<StreamEvents> {
 
 	private connectToChannel<C extends keyof Channels>(channel: C, params: Channels[C]['params']): NonSharedConnection<Channels[C]> {
 		const connection = new NonSharedConnection(this, channel, this.genId(), params);
-		this.nonSharedConnections.push(connection);
+		this.nonSharedConnections.push(connection as unknown as NonSharedConnection);
 		return connection;
 	}
 
@@ -174,9 +176,9 @@ export default class Stream extends EventEmitter<StreamEvents> {
 	 * ! ストリーム上のやり取りはすべてJSONで行われます !
 	 */
 	public send(typeOrPayload: string): void
-	public send(typeOrPayload: string, payload: any): void
-	public send(typeOrPayload: Record<string, any> | any[]): void
-	public send(typeOrPayload: string | Record<string, any> | any[], payload?: any): void {
+	public send(typeOrPayload: string, payload: unknown): void
+	public send(typeOrPayload: Record<string, unknown> | unknown[]): void
+	public send(typeOrPayload: string | Record<string, unknown> | unknown[], payload?: unknown): void {
 		if (typeof typeOrPayload === 'string') {
 			this.stream.send(JSON.stringify({
 				type: typeOrPayload,
@@ -211,7 +213,7 @@ class Pool {
 	public id: string;
 	protected stream: Stream;
 	public users = 0;
-	private disposeTimerId: any;
+	private disposeTimerId: ReturnType<typeof setTimeout> | null = null;
 	private isConnected = false;
 
 	constructor(stream: Stream, channel: string, id: string) {
@@ -275,7 +277,7 @@ class Pool {
 	}
 }
 
-export abstract class Connection<Channel extends AnyOf<Channels> = any> extends EventEmitter<Channel['events']> {
+export abstract class Connection<Channel extends AnyOf<Channels> = AnyOf<Channels>> extends EventEmitter<Channel['events']> {
 	public channel: string;
 	protected stream: Stream;
 	public abstract id: string;
@@ -309,7 +311,7 @@ export abstract class Connection<Channel extends AnyOf<Channels> = any> extends
 	public abstract dispose(): void;
 }
 
-class SharedConnection<Channel extends AnyOf<Channels> = any> extends Connection<Channel> {
+class SharedConnection<Channel extends AnyOf<Channels> = AnyOf<Channels>> extends Connection<Channel> {
 	private pool: Pool;
 
 	public get id(): string {
@@ -328,11 +330,11 @@ class SharedConnection<Channel extends AnyOf<Channels> = any> extends Connection
 	public dispose(): void {
 		this.pool.dec();
 		this.removeAllListeners();
-		this.stream.removeSharedConnection(this);
+		this.stream.removeSharedConnection(this as unknown as SharedConnection);
 	}
 }
 
-class NonSharedConnection<Channel extends AnyOf<Channels> = any> extends Connection<Channel> {
+class NonSharedConnection<Channel extends AnyOf<Channels> = AnyOf<Channels>> extends Connection<Channel> {
 	public id: string;
 	protected params: Channel['params'];
 
@@ -359,6 +361,6 @@ class NonSharedConnection<Channel extends AnyOf<Channels> = any> extends Connect
 	public dispose(): void {
 		this.removeAllListeners();
 		this.stream.send('disconnect', { id: this.id });
-		this.stream.disconnectToChannel(this);
+		this.stream.disconnectToChannel(this as unknown as NonSharedConnection);
 	}
 }
diff --git a/packages/misskey-js/src/streaming.types.ts b/packages/misskey-js/src/streaming.types.ts
index 9a86e03d69..4447a2e8fc 100644
--- a/packages/misskey-js/src/streaming.types.ts
+++ b/packages/misskey-js/src/streaming.types.ts
@@ -21,6 +21,14 @@ import {
 	ServerStatsLog,
 	ReversiGameDetailed,
 } from './entities.js';
+import {
+	ReversiUpdateKey,
+} from './consts.js';
+
+type ReversiUpdateSettings<K extends ReversiUpdateKey> = {
+	key: K;
+	value: ReversiGameDetailed[K];
+};
 
 export type Channels = {
 	main: {
@@ -51,6 +59,7 @@ export type Channels = {
 			registryUpdated: (payload: {
 				scope?: string[];
 				key: string;
+				// eslint-disable-next-line @typescript-eslint/no-explicit-any
 				value: any | null;
 			}) => void;
 			driveFileCreated: (payload: DriveFile) => void;
@@ -208,8 +217,8 @@ export type Channels = {
 			ended: (payload: { winnerId: User['id'] | null; game: ReversiGameDetailed; }) => void;
 			canceled: (payload: { userId: User['id']; }) => void;
 			changeReadyStates: (payload: { user1: boolean; user2: boolean; }) => void;
-			updateSettings: (payload: { userId: User['id']; key: string; value: any; }) => void;
-			log: (payload: Record<string, any>) => void;
+			updateSettings: <K extends ReversiUpdateKey>(payload: { userId: User['id']; key: K; value: ReversiGameDetailed[K]; }) => void;
+			log: (payload: Record<string, unknown>) => void;
 		};
 		receives: {
 			putStone: {
@@ -218,10 +227,7 @@ export type Channels = {
 			};
 			ready: boolean;
 			cancel: null | Record<string, never>;
-			updateSettings: {
-				key: string;
-				value: any;
-			};
+			updateSettings: ReversiUpdateSettings<ReversiUpdateKey>;
 			claimTimeIsUp: null | Record<string, never>;
 		}
 	}