From 987c98faecac7dd1c0747cf0351315b6f18d0c5c Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 5 Sep 2021 23:47:41 +0900
Subject: [PATCH 01/91] wip

---
 package.json                   |  1 +
 src/server/api/stream/index.ts |  9 +++---
 src/server/api/stream/types.ts | 50 ++++++++++++++++++++++++++++++++++
 yarn.lock                      |  5 ++++
 4 files changed, 61 insertions(+), 4 deletions(-)
 create mode 100644 src/server/api/stream/types.ts

diff --git a/package.json b/package.json
index ebb4d90897..0f93ae03e9 100644
--- a/package.json
+++ b/package.json
@@ -209,6 +209,7 @@
 		"seedrandom": "3.0.5",
 		"sharp": "0.29.0",
 		"speakeasy": "2.0.0",
+		"strict-event-emitter-types": "2.0.0",
 		"stringz": "2.1.0",
 		"style-loader": "3.2.1",
 		"summaly": "2.4.1",
diff --git a/src/server/api/stream/index.ts b/src/server/api/stream/index.ts
index 469f28f11c..13011b193e 100644
--- a/src/server/api/stream/index.ts
+++ b/src/server/api/stream/index.ts
@@ -15,6 +15,7 @@ import { UserProfile } from '@/models/entities/user-profile';
 import { publishChannelStream, publishGroupMessagingStream, publishMessagingStream } from '@/services/stream';
 import { UserGroup } from '@/models/entities/user-group';
 import { PackedNote } from '@/models/repositories/note';
+import { StreamEventEmitter, UserEvent } from './types';
 
 /**
  * Main stream connection
@@ -28,7 +29,7 @@ export default class Connection {
 	public followingChannels: Set<ChannelModel['id']> = new Set();
 	public token?: AccessToken;
 	private wsConnection: websocket.connection;
-	public subscriber: EventEmitter;
+	public subscriber: StreamEventEmitter;
 	private channels: Channel[] = [];
 	private subscribingNotes: any = {};
 	private cachedNotes: PackedNote[] = [];
@@ -57,14 +58,14 @@ export default class Connection {
 			this.updateFollowingChannels();
 			this.updateUserProfile();
 
-			this.subscriber.on(`user:${this.user.id}`, ({ type, body }) => {
-				this.onUserEvent(type, body);
+			this.subscriber.on(`user:${this.user.id}`, (ev) => {
+				this.onUserEvent(ev);
 			});
 		}
 	}
 
 	@autobind
-	private onUserEvent(type: string, body: any) {
+	private onUserEvent({ type, body }: UserEvent) {
 		switch (type) {
 			case 'follow':
 				this.following.add(body.id);
diff --git a/src/server/api/stream/types.ts b/src/server/api/stream/types.ts
new file mode 100644
index 0000000000..92b3f0fdb5
--- /dev/null
+++ b/src/server/api/stream/types.ts
@@ -0,0 +1,50 @@
+import { User } from '@/models/entities/user';
+import { EventEmitter } from 'events';
+import Emitter from 'strict-event-emitter-types';
+import StreamTypes from 'misskey-js/built/streaming.types';
+import { Channel } from '@/models/entities/channel';
+import { UserProfile } from '@/models/entities/user-profile';
+import { PackedUser } from '@/models/repositories/user';
+
+type Payload<T extends (payload: any) => void> = T extends (payload: infer P) => void ? P : never;
+
+// https://stackoverflow.com/questions/49311989/can-i-infer-the-type-of-a-value-using-extends-keyof-type
+type EventUnionFromDictionary<
+	T extends object,
+	U = { [K in keyof T]: { type: K; body: T[K]} }
+> = U[keyof U];
+
+export type BroadcastStream<T extends keyof StreamTypes.BroadcasrEvents> = {
+	name: 'broadcast';
+	type: T;
+	body: Payload<StreamTypes.BroadcasrEvents[T]>;
+};
+
+export interface UserEventTypes {
+	terminate: {};
+	followChannel: Channel;
+	unfollowChannel: Channel;
+	updateUserProfile: UserProfile;
+	mute: User;
+	unmute: User;
+	follow: PackedUser;
+	unfollow: PackedUser;
+	userAdded: PackedUser;
+};
+
+// UserList userRemoved: PackedUser;
+
+export type UserEventName = `user:${User['id']}`;
+export type UserEvent = EventUnionFromDictionary<UserEventTypes>;
+
+interface StreamEvents {
+	'broadcast': <T extends keyof StreamTypes.BroadcasrEvents>(e: BroadcastStream<T>) => void;
+}
+
+interface AuthenticatedStreamEvents {
+	[key: UserEventName]: <T extends keyof UserEventTypes>(e: UserEvent<T>) => void;
+	[key: `mainStream:${User['id']}`]: (e: { type: string; body: any }) => void;
+	[key: `driveStream:${User['id']}`]: (e: { type: string; body: any }) => void;
+}
+
+export type StreamEventEmitter = Emitter<EventEmitter, AuthenticatedStreamEvents & StreamEvents>;
diff --git a/yarn.lock b/yarn.lock
index 8151dfe089..33f9f22454 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -10249,6 +10249,11 @@ streamsearch@0.1.2:
   resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a"
   integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=
 
+strict-event-emitter-types@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/strict-event-emitter-types/-/strict-event-emitter-types-2.0.0.tgz#05e15549cb4da1694478a53543e4e2f4abcf277f"
+  integrity sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA==
+
 strict-uri-encode@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"

From 225805da8a524611f273384517f19217a6696346 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 5 Sep 2021 23:55:01 +0900
Subject: [PATCH 02/91] wip

---
 src/server/api/stream/types.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/server/api/stream/types.ts b/src/server/api/stream/types.ts
index 92b3f0fdb5..74a0db8e53 100644
--- a/src/server/api/stream/types.ts
+++ b/src/server/api/stream/types.ts
@@ -42,7 +42,7 @@ interface StreamEvents {
 }
 
 interface AuthenticatedStreamEvents {
-	[key: UserEventName]: <T extends keyof UserEventTypes>(e: UserEvent<T>) => void;
+	[key: UserEventName]: (e: UserEvent) => void;
 	[key: `mainStream:${User['id']}`]: (e: { type: string; body: any }) => void;
 	[key: `driveStream:${User['id']}`]: (e: { type: string; body: any }) => void;
 }

From f74f4ffc6acd46e453f1c0b9234b5248513cfc6a Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 5 Sep 2021 23:55:32 +0900
Subject: [PATCH 03/91] wip

---
 src/server/api/stream/index.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/server/api/stream/index.ts b/src/server/api/stream/index.ts
index 13011b193e..e9f5bf9d15 100644
--- a/src/server/api/stream/index.ts
+++ b/src/server/api/stream/index.ts
@@ -58,7 +58,7 @@ export default class Connection {
 			this.updateFollowingChannels();
 			this.updateUserProfile();
 
-			this.subscriber.on(`user:${this.user.id}`, (ev) => {
+			this.subscriber.on(`user:${this.user.id}`, ev => {
 				this.onUserEvent(ev);
 			});
 		}

From 08576c49de16435d980a2724e980c138515b578b Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 Sep 2021 00:11:31 +0900
Subject: [PATCH 04/91] :v:

---
 src/server/api/stream/index.ts | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/src/server/api/stream/index.ts b/src/server/api/stream/index.ts
index e9f5bf9d15..c2f0870f11 100644
--- a/src/server/api/stream/index.ts
+++ b/src/server/api/stream/index.ts
@@ -65,36 +65,36 @@ export default class Connection {
 	}
 
 	@autobind
-	private onUserEvent({ type, body }: UserEvent) {
-		switch (type) {
+	private onUserEvent(ev: UserEvent) { // { type, body }と展開すると型も展開されてしまう
+		switch (ev.type) {
 			case 'follow':
-				this.following.add(body.id);
+				this.following.add(ev.body.id);
 				break;
 
 			case 'unfollow':
-				this.following.delete(body.id);
+				this.following.delete(ev.body.id);
 				break;
 
 			case 'mute':
-				this.muting.add(body.id);
+				this.muting.add(ev.body.id);
 				break;
 
 			case 'unmute':
-				this.muting.delete(body.id);
+				this.muting.delete(ev.body.id);
 				break;
 
 			// TODO: block events
 
 			case 'followChannel':
-				this.followingChannels.add(body.id);
+				this.followingChannels.add(ev.body.id);
 				break;
 
 			case 'unfollowChannel':
-				this.followingChannels.delete(body.id);
+				this.followingChannels.delete(ev.body.id);
 				break;
 
 			case 'updateUserProfile':
-				this.userProfile = body;
+				this.userProfile = ev.body;
 				break;
 
 			case 'terminate':

From 06c0373be924d5b970353746ddba7ce4f4428d5c Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 Sep 2021 00:55:40 +0900
Subject: [PATCH 05/91] add main stream

---
 src/server/api/stream/channels/main.ts | 25 ++++++------
 src/server/api/stream/index.ts         |  4 +-
 src/server/api/stream/types.ts         | 53 ++++++++++++++++++++------
 3 files changed, 55 insertions(+), 27 deletions(-)

diff --git a/src/server/api/stream/channels/main.ts b/src/server/api/stream/channels/main.ts
index b99cb931da..d1dce76dd9 100644
--- a/src/server/api/stream/channels/main.ts
+++ b/src/server/api/stream/channels/main.ts
@@ -11,35 +11,34 @@ export default class extends Channel {
 	public async init(params: any) {
 		// Subscribe main stream channel
 		this.subscriber.on(`mainStream:${this.user!.id}`, async data => {
-			const { type } = data;
-			let { body } = data;
-
-			switch (type) {
+			switch (data.type) {
 				case 'notification': {
-					if (this.muting.has(body.userId)) return;
-					if (body.note && body.note.isHidden) {
-						const note = await Notes.pack(body.note.id, this.user, {
+					if (data.body.userId && this.muting.has(data.body.userId)) return;
+
+					// ????
+					if (data.body.note && data.body.note.isHidden) {
+						const note = await Notes.pack(data.body.note.id, this.user, {
 							detail: true
 						});
 						this.connection.cacheNote(note);
-						body.note = note;
+						data.body.note = note;
 					}
 					break;
 				}
 				case 'mention': {
-					if (this.muting.has(body.userId)) return;
-					if (body.isHidden) {
-						const note = await Notes.pack(body.id, this.user, {
+					if (this.muting.has(data.body.userId)) return;
+					if (data.body.isHidden) {
+						const note = await Notes.pack(data.body.id, this.user, {
 							detail: true
 						});
 						this.connection.cacheNote(note);
-						body = note;
+						data.body = note;
 					}
 					break;
 				}
 			}
 
-			this.send(type, body);
+			this.send(data.type, data.body);
 		});
 	}
 }
diff --git a/src/server/api/stream/index.ts b/src/server/api/stream/index.ts
index c2f0870f11..5bd021c814 100644
--- a/src/server/api/stream/index.ts
+++ b/src/server/api/stream/index.ts
@@ -15,7 +15,7 @@ import { UserProfile } from '@/models/entities/user-profile';
 import { publishChannelStream, publishGroupMessagingStream, publishMessagingStream } from '@/services/stream';
 import { UserGroup } from '@/models/entities/user-group';
 import { PackedNote } from '@/models/repositories/note';
-import { StreamEventEmitter, UserEvent } from './types';
+import { StreamEventEmitter, UserEvents } from './types';
 
 /**
  * Main stream connection
@@ -65,7 +65,7 @@ export default class Connection {
 	}
 
 	@autobind
-	private onUserEvent(ev: UserEvent) { // { type, body }と展開すると型も展開されてしまう
+	private onUserEvent(ev: UserEvents) { // { type, body }と展開すると型も展開されてしまう
 		switch (ev.type) {
 			case 'follow':
 				this.following.add(ev.body.id);
diff --git a/src/server/api/stream/types.ts b/src/server/api/stream/types.ts
index 74a0db8e53..549af96fa8 100644
--- a/src/server/api/stream/types.ts
+++ b/src/server/api/stream/types.ts
@@ -5,6 +5,8 @@ import StreamTypes from 'misskey-js/built/streaming.types';
 import { Channel } from '@/models/entities/channel';
 import { UserProfile } from '@/models/entities/user-profile';
 import { PackedUser } from '@/models/repositories/user';
+import { PackedNotification } from '@/models/repositories/notification';
+import { PackedNote } from '@/models/repositories/note';
 
 type Payload<T extends (payload: any) => void> = T extends (payload: infer P) => void ? P : never;
 
@@ -14,11 +16,12 @@ type EventUnionFromDictionary<
 	U = { [K in keyof T]: { type: K; body: T[K]} }
 > = U[keyof U];
 
-export type BroadcastStream<T extends keyof StreamTypes.BroadcasrEvents> = {
-	name: 'broadcast';
-	type: T;
-	body: Payload<StreamTypes.BroadcasrEvents[T]>;
-};
+type EventUnionFromMkJSTypes<
+	T extends { [key: string]: ((payload: any) => void) | (() => void) },
+	U = { [K in keyof T]: { type: K; body: Payload<T[K]>} }
+> = U[keyof U]
+
+export type BroadcastStream = EventUnionFromMkJSTypes<StreamTypes.BroadcasrEvents>;
 
 export interface UserEventTypes {
 	terminate: {};
@@ -31,19 +34,45 @@ export interface UserEventTypes {
 	unfollow: PackedUser;
 	userAdded: PackedUser;
 };
-
-// UserList userRemoved: PackedUser;
-
 export type UserEventName = `user:${User['id']}`;
-export type UserEvent = EventUnionFromDictionary<UserEventTypes>;
+export type UserEvents = EventUnionFromDictionary<UserEventTypes>;
+
+export interface mainStreamTypes {
+	notification: PackedNotification;
+	mention: PackedNote;
+	reply: PackedNote;
+	renote: PackedNote;
+	follow: PackedUser;
+	followed: PackedUser;
+	unfollow: PackedUser;
+	meUpdated: PackedUser;
+	pageEvent: Payload<StreamTypes.Channels['main']['events']['pageEvent']>;
+	urlUploadFinished: Payload<StreamTypes.Channels['main']['events']['urlUploadFinished']>;
+	readAllNotifications: never;
+	unreadNotification: never;
+	unreadMention: never;
+	readAllUnreadMentions: never;
+	unreadSpecifiedNote: never;
+	readAllUnreadSpecifiedNotes: never;
+	readAllMessagingMessages: never;
+	unreadMessagingMessage: never;
+	readAllAntennas: never;
+	unreadAntenna: never;
+	readAllAnnouncements: never;
+	readAllChannels: never;
+	unreadChannel: never;
+	myTokenRegenerated: never;
+};
+export type mainStreamName = `mainStream:${User['id']}`;
+export type mainStreams = EventUnionFromDictionary<mainStreamTypes>;
 
 interface StreamEvents {
-	'broadcast': <T extends keyof StreamTypes.BroadcasrEvents>(e: BroadcastStream<T>) => void;
+	'broadcast': (e: BroadcastStream) => void;
 }
 
 interface AuthenticatedStreamEvents {
-	[key: UserEventName]: (e: UserEvent) => void;
-	[key: `mainStream:${User['id']}`]: (e: { type: string; body: any }) => void;
+	[key: UserEventName]: (e: UserEvents) => void;
+	[key: mainStreamName]: (e: mainStreams) => void;
 	[key: `driveStream:${User['id']}`]: (e: { type: string; body: any }) => void;
 }
 

From ba1c57c7bd29232e2054565ee768052149268801 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 Sep 2021 01:23:30 +0900
Subject: [PATCH 06/91] =?UTF-8?q?packedNotificationSchema=E3=82=92?=
 =?UTF-8?q?=E6=9B=B4=E6=96=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/models/repositories/notification.ts | 34 ++++++++++++++++++++-----
 1 file changed, 28 insertions(+), 6 deletions(-)

diff --git a/src/models/repositories/notification.ts b/src/models/repositories/notification.ts
index 55af96b6d7..4d13940c76 100644
--- a/src/models/repositories/notification.ts
+++ b/src/models/repositories/notification.ts
@@ -7,6 +7,7 @@ import { Note } from '@/models/entities/note';
 import { NoteReaction } from '@/models/entities/note-reaction';
 import { User } from '@/models/entities/user';
 import { aggregateNoteEmojis, prefetchEmojis } from '@/misc/populate-emojis';
+import { notificationTypes } from '@/types';
 
 export type PackedNotification = SchemaType<typeof packedNotificationSchema>;
 
@@ -124,20 +125,41 @@ export const packedNotificationSchema = {
 			optional: false as const, nullable: false as const,
 			format: 'date-time',
 		},
+		isRead: {
+			type: 'boolean' as const,
+			optional: false as const, nullable: false as const,
+		},
 		type: {
 			type: 'string' as const,
 			optional: false as const, nullable: false as const,
-			enum: ['follow', 'followRequestAccepted', 'receiveFollowRequest', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote'],
-		},
-		userId: {
-			type: 'string' as const,
-			optional: true as const, nullable: true as const,
-			format: 'id',
+			enum: [...notificationTypes],
 		},
 		user: {
 			type: 'object' as const,
 			ref: 'User',
 			optional: true as const, nullable: true as const,
 		},
+		userId: {
+			type: 'string' as const,
+			optional: true as const, nullable: true as const,
+			format: 'id',
+		},
+		note: {
+			type: 'object' as const,
+			ref: 'Note',
+			optional: true as const, nullable: true as const,
+		},
+		reaction: {
+			type: 'string' as const,
+			optional: true as const, nullable: true as const,
+		},
+		body: {
+			type: 'string' as const,
+			optional: true as const, nullable: true as const,
+		},
+		icon: {
+			type: 'string' as const,
+			optional: true as const, nullable: true as const,
+		},
 	}
 };

From 9a089e584e88186ca1fd49db5ca17e23be8be7a8 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 Sep 2021 01:23:50 +0900
Subject: [PATCH 07/91] =?UTF-8?q?read:gallery,=20write:gallery,=20read:gal?=
 =?UTF-8?q?lery-likes,=20write:gallery-likes=E3=81=AB=E7=BF=BB=E8=A8=B3?=
 =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 locales/ja-JP.yml           | 4 ++++
 src/misc/api-permissions.ts | 1 +
 2 files changed, 5 insertions(+)

diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index e22f50668f..e5747d5124 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1150,6 +1150,10 @@ _permissions:
   "write:user-groups": "ユーザーグループを操作する"
   "read:channels": "チャンネルを見る"
   "write:channels": "チャンネルを操作する"
+	"read:gallery": "ギャラリーを見る"
+	"write:gallery": "ギャラリーを操作する"
+	"read:gallery-likes": "ギャラリーのいいねを見る"
+	"write:gallery-likes": "ギャラリーのいいねを操作する"
 
 _auth:
   shareAccess: "「{name}」がアカウントにアクセスすることを許可しますか?"
diff --git a/src/misc/api-permissions.ts b/src/misc/api-permissions.ts
index eb20c3d289..160cdf9fd6 100644
--- a/src/misc/api-permissions.ts
+++ b/src/misc/api-permissions.ts
@@ -32,3 +32,4 @@ export const kinds = [
 	'read:gallery-likes',
 	'write:gallery-likes',
 ];
+// IF YOU ADD KINDS(PERMISSIONS), YOU MUST ADD TRANSLATIONS (under _permissions).

From 3bde3e523dedb0b7620cd4ef603f3d50315db7c0 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 Sep 2021 01:35:00 +0900
Subject: [PATCH 08/91] fix

---
 locales/ja-JP.yml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index e5747d5124..b9623ef0d0 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1150,10 +1150,10 @@ _permissions:
   "write:user-groups": "ユーザーグループを操作する"
   "read:channels": "チャンネルを見る"
   "write:channels": "チャンネルを操作する"
-	"read:gallery": "ギャラリーを見る"
-	"write:gallery": "ギャラリーを操作する"
-	"read:gallery-likes": "ギャラリーのいいねを見る"
-	"write:gallery-likes": "ギャラリーのいいねを操作する"
+  "read:gallery": "ギャラリーを見る"
+  "write:gallery": "ギャラリーを操作する"
+  "read:gallery-likes": "ギャラリーのいいねを見る"
+  "write:gallery-likes": "ギャラリーのいいねを操作する"
 
 _auth:
   shareAccess: "「{name}」がアカウントにアクセスすることを許可しますか?"

From 110de167e8e440f50e00765e302502f54b14eb12 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 Sep 2021 01:38:48 +0900
Subject: [PATCH 09/91] ok

---
 src/server/api/stream/channels/main.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/server/api/stream/channels/main.ts b/src/server/api/stream/channels/main.ts
index d1dce76dd9..131ac30472 100644
--- a/src/server/api/stream/channels/main.ts
+++ b/src/server/api/stream/channels/main.ts
@@ -15,7 +15,6 @@ export default class extends Channel {
 				case 'notification': {
 					if (data.body.userId && this.muting.has(data.body.userId)) return;
 
-					// ????
 					if (data.body.note && data.body.note.isHidden) {
 						const note = await Notes.pack(data.body.note.id, this.user, {
 							detail: true

From 47e0e32aa31c0d4e3da85da2294a1503f3a5588b Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 Sep 2021 01:58:07 +0900
Subject: [PATCH 10/91] add header, choice, invitation

---
 src/models/repositories/notification.ts | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/src/models/repositories/notification.ts b/src/models/repositories/notification.ts
index 4d13940c76..ed9de7ef4c 100644
--- a/src/models/repositories/notification.ts
+++ b/src/models/repositories/notification.ts
@@ -153,10 +153,22 @@ export const packedNotificationSchema = {
 			type: 'string' as const,
 			optional: true as const, nullable: true as const,
 		},
+		choice: {
+			type: 'number' as const,
+			optional: true as const, nullable: true as const,
+		},
+		invitation: {
+			type: 'object' as const,
+			optional: true as const, nullable: true as const,
+		},
 		body: {
 			type: 'string' as const,
 			optional: true as const, nullable: true as const,
 		},
+		header: {
+			type: 'string' as const,
+			optional: true as const, nullable: true as const,
+		},
 		icon: {
 			type: 'string' as const,
 			optional: true as const, nullable: true as const,

From 75015c70446ac131955fa8ca592607e6c7ae3d9d Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 Sep 2021 01:58:07 +0900
Subject: [PATCH 11/91] add header, choice, invitation

---
 src/models/repositories/notification.ts | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/src/models/repositories/notification.ts b/src/models/repositories/notification.ts
index 4d13940c76..ed9de7ef4c 100644
--- a/src/models/repositories/notification.ts
+++ b/src/models/repositories/notification.ts
@@ -153,10 +153,22 @@ export const packedNotificationSchema = {
 			type: 'string' as const,
 			optional: true as const, nullable: true as const,
 		},
+		choice: {
+			type: 'number' as const,
+			optional: true as const, nullable: true as const,
+		},
+		invitation: {
+			type: 'object' as const,
+			optional: true as const, nullable: true as const,
+		},
 		body: {
 			type: 'string' as const,
 			optional: true as const, nullable: true as const,
 		},
+		header: {
+			type: 'string' as const,
+			optional: true as const, nullable: true as const,
+		},
 		icon: {
 			type: 'string' as const,
 			optional: true as const, nullable: true as const,

From ee03cd616020725e0f0478731704bfbfbe990efc Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 Sep 2021 02:33:16 +0900
Subject: [PATCH 12/91] test

---
 src/misc/schema.ts                      | 13 ++++++++++++-
 src/models/repositories/blocking.ts     |  2 +-
 src/models/repositories/notification.ts | 13 +++++++++++++
 3 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/src/misc/schema.ts b/src/misc/schema.ts
index e14e6e0dd7..8854ab5303 100644
--- a/src/misc/schema.ts
+++ b/src/misc/schema.ts
@@ -1,3 +1,6 @@
+import { packedNoteSchema } from "@/models/repositories/note";
+import { packedNotificationSchema } from "@/models/repositories/notification";
+
 export type Schema = {
 	type: 'boolean' | 'number' | 'string' | 'array' | 'object' | 'any';
 	nullable: boolean;
@@ -43,11 +46,19 @@ type NullOrUndefined<p extends Schema, T> =
 			? (T | undefined)
 			: T;
 
+export const refs = {
+	Note: packedNoteSchema,
+	Notification: packedNotificationSchema,
+};
+
 export type SchemaType<p extends Schema> =
 	p['type'] extends 'number' ? NullOrUndefined<p, number> :
 	p['type'] extends 'string' ? NullOrUndefined<p, string> :
 	p['type'] extends 'boolean' ? NullOrUndefined<p, boolean> :
 	p['type'] extends 'array' ? NullOrUndefined<p, MyType<NonNullable<p['items']>>[]> :
-	p['type'] extends 'object' ? NullOrUndefined<p, ObjType<NonNullable<p['properties']>>> :
+	p['type'] extends 'object' ?
+	(	p['ref'] extends keyof typeof refs ?
+		NullOrUndefined<p, SchemaType<typeof refs[p['ref']]>> :
+		NullOrUndefined<p, ObjType<NonNullable<p['properties']>>> ) :
 	p['type'] extends 'any' ? NullOrUndefined<p, any> :
 	any;
diff --git a/src/models/repositories/blocking.ts b/src/models/repositories/blocking.ts
index dd3a10905c..515b3a6b16 100644
--- a/src/models/repositories/blocking.ts
+++ b/src/models/repositories/blocking.ts
@@ -56,7 +56,7 @@ export const packedBlockingSchema = {
 		blockee: {
 			type: 'object' as const,
 			optional: false as const, nullable: false as const,
-			ref: 'User',
+			ref: 'User' as const,
 		},
 	}
 };
diff --git a/src/models/repositories/notification.ts b/src/models/repositories/notification.ts
index ed9de7ef4c..584e3d8061 100644
--- a/src/models/repositories/notification.ts
+++ b/src/models/repositories/notification.ts
@@ -145,6 +145,19 @@ export const packedNotificationSchema = {
 			format: 'id',
 		},
 		note: {
+			type: 'object' as const,
+			ref: 'Note' as const,
+			optional: true as const, nullable: true as const,
+		},
+		reaction: {
+			type: 'string' as const,
+			optional: true as const, nullable: true as const,
+		},
+		choice: {
+			type: 'number' as const,
+			optional: true as const, nullable: true as const,
+		},
+		invitation: {
 			type: 'object' as const,
 			ref: 'Note',
 			optional: true as const, nullable: true as const,

From 7ab97004d9d3630218e8f4a1754e2db1b0510cb4 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 Sep 2021 02:49:21 +0900
Subject: [PATCH 13/91] fix

---
 src/models/repositories/notification.ts | 12 ------------
 1 file changed, 12 deletions(-)

diff --git a/src/models/repositories/notification.ts b/src/models/repositories/notification.ts
index 584e3d8061..983fdaf6e8 100644
--- a/src/models/repositories/notification.ts
+++ b/src/models/repositories/notification.ts
@@ -162,18 +162,6 @@ export const packedNotificationSchema = {
 			ref: 'Note',
 			optional: true as const, nullable: true as const,
 		},
-		reaction: {
-			type: 'string' as const,
-			optional: true as const, nullable: true as const,
-		},
-		choice: {
-			type: 'number' as const,
-			optional: true as const, nullable: true as const,
-		},
-		invitation: {
-			type: 'object' as const,
-			optional: true as const, nullable: true as const,
-		},
 		body: {
 			type: 'string' as const,
 			optional: true as const, nullable: true as const,

From d200424c6783b46906f520585ebbb763e291fd1c Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 Sep 2021 02:49:38 +0900
Subject: [PATCH 14/91] fix

---
 src/models/repositories/notification.ts | 12 ------------
 1 file changed, 12 deletions(-)

diff --git a/src/models/repositories/notification.ts b/src/models/repositories/notification.ts
index 584e3d8061..983fdaf6e8 100644
--- a/src/models/repositories/notification.ts
+++ b/src/models/repositories/notification.ts
@@ -162,18 +162,6 @@ export const packedNotificationSchema = {
 			ref: 'Note',
 			optional: true as const, nullable: true as const,
 		},
-		reaction: {
-			type: 'string' as const,
-			optional: true as const, nullable: true as const,
-		},
-		choice: {
-			type: 'number' as const,
-			optional: true as const, nullable: true as const,
-		},
-		invitation: {
-			type: 'object' as const,
-			optional: true as const, nullable: true as const,
-		},
 		body: {
 			type: 'string' as const,
 			optional: true as const, nullable: true as const,

From c86ac1c6eca687f510fbbea26ab8b5040eadc0ab Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 Sep 2021 03:26:19 +0900
Subject: [PATCH 15/91] yatta

---
 src/misc/schema.ts                           | 66 ++++++++++++++++----
 src/models/repositories/clip.ts              |  2 +-
 src/models/repositories/drive-file.ts        |  4 +-
 src/models/repositories/drive-folder.ts      |  2 +-
 src/models/repositories/following.ts         |  4 +-
 src/models/repositories/gallery-post.ts      |  6 +-
 src/models/repositories/messaging-message.ts |  8 +--
 src/models/repositories/muting.ts            |  2 +-
 src/models/repositories/note-favorite.ts     |  2 +-
 src/models/repositories/note-reaction.ts     |  2 +-
 src/models/repositories/note.ts              | 12 ++--
 src/models/repositories/notification.ts      |  3 +-
 src/models/repositories/page.ts              |  2 +-
 src/models/repositories/user.ts              |  4 +-
 src/server/api/openapi/schemas.ts            | 49 ++-------------
 15 files changed, 84 insertions(+), 84 deletions(-)

diff --git a/src/misc/schema.ts b/src/misc/schema.ts
index 8854ab5303..c4ca80249d 100644
--- a/src/misc/schema.ts
+++ b/src/misc/schema.ts
@@ -1,5 +1,50 @@
-import { packedNoteSchema } from "@/models/repositories/note";
-import { packedNotificationSchema } from "@/models/repositories/notification";
+import { packedUserSchema } from '@/models/repositories/user';
+import { packedNoteSchema } from '@/models/repositories/note';
+import { packedUserListSchema } from '@/models/repositories/user-list';
+import { packedAppSchema } from '@/models/repositories/app';
+import { packedMessagingMessageSchema } from '@/models/repositories/messaging-message';
+import { packedNotificationSchema } from '@/models/repositories/notification';
+import { packedDriveFileSchema } from '@/models/repositories/drive-file';
+import { packedDriveFolderSchema } from '@/models/repositories/drive-folder';
+import { packedFollowingSchema } from '@/models/repositories/following';
+import { packedMutingSchema } from '@/models/repositories/muting';
+import { packedBlockingSchema } from '@/models/repositories/blocking';
+import { packedNoteReactionSchema } from '@/models/repositories/note-reaction';
+import { packedHashtagSchema } from '@/models/repositories/hashtag';
+import { packedPageSchema } from '@/models/repositories/page';
+import { packedUserGroupSchema } from '@/models/repositories/user-group';
+import { packedNoteFavoriteSchema } from '@/models/repositories/note-favorite';
+import { packedChannelSchema } from '@/models/repositories/channel';
+import { packedAntennaSchema } from '@/models/repositories/antenna';
+import { packedClipSchema } from '@/models/repositories/clip';
+import { packedFederationInstanceSchema } from '@/models/repositories/federation-instance';
+import { packedQueueCountSchema } from '@/models/repositories/queue';
+import { packedGalleryPostSchema } from '@/models/repositories/gallery-post';
+
+export const refs = {
+	User: packedUserSchema,
+	UserList: packedUserListSchema,
+	UserGroup: packedUserGroupSchema,
+	App: packedAppSchema,
+	MessagingMessage: packedMessagingMessageSchema,
+	Note: packedNoteSchema,
+	NoteReaction: packedNoteReactionSchema,
+	NoteFavorite: packedNoteFavoriteSchema,
+	Notification: packedNotificationSchema,
+	DriveFile: packedDriveFileSchema,
+	DriveFolder: packedDriveFolderSchema,
+	Following: packedFollowingSchema,
+	Muting: packedMutingSchema,
+	Blocking: packedBlockingSchema,
+	Hashtag: packedHashtagSchema,
+	Page: packedPageSchema,
+	Channel: packedChannelSchema,
+	QueueCount: packedQueueCountSchema,
+	Antenna: packedAntennaSchema,
+	Clip: packedClipSchema,
+	FederationInstance: packedFederationInstanceSchema,
+	GalleryPost: packedGalleryPostSchema,
+};
 
 export type Schema = {
 	type: 'boolean' | 'number' | 'string' | 'array' | 'object' | 'any';
@@ -10,8 +55,9 @@ export type Schema = {
 	description?: string;
 	example?: any;
 	format?: string;
-	ref?: string;
+	ref?: keyof typeof refs;
 	enum?: string[];
+	default?: boolean | null;
 };
 
 type NonUndefinedPropertyNames<T extends Obj> = {
@@ -46,19 +92,15 @@ type NullOrUndefined<p extends Schema, T> =
 			? (T | undefined)
 			: T;
 
-export const refs = {
-	Note: packedNoteSchema,
-	Notification: packedNotificationSchema,
-};
-
 export type SchemaType<p extends Schema> =
 	p['type'] extends 'number' ? NullOrUndefined<p, number> :
 	p['type'] extends 'string' ? NullOrUndefined<p, string> :
 	p['type'] extends 'boolean' ? NullOrUndefined<p, boolean> :
 	p['type'] extends 'array' ? NullOrUndefined<p, MyType<NonNullable<p['items']>>[]> :
-	p['type'] extends 'object' ?
-	(	p['ref'] extends keyof typeof refs ?
-		NullOrUndefined<p, SchemaType<typeof refs[p['ref']]>> :
-		NullOrUndefined<p, ObjType<NonNullable<p['properties']>>> ) :
+	p['type'] extends 'object' ? (
+		p['ref'] extends keyof typeof refs
+			? NullOrUndefined<p, SchemaType<typeof refs[p['ref']]>>
+			: NullOrUndefined<p, ObjType<NonNullable<p['properties']>>>
+	) :
 	p['type'] extends 'any' ? NullOrUndefined<p, any> :
 	any;
diff --git a/src/models/repositories/clip.ts b/src/models/repositories/clip.ts
index 49dc3a332e..e3d718bef4 100644
--- a/src/models/repositories/clip.ts
+++ b/src/models/repositories/clip.ts
@@ -53,7 +53,7 @@ export const packedClipSchema = {
 		},
 		user: {
 			type: 'object' as const,
-			ref: 'User',
+			ref: 'User' as const,
 			optional: false as const, nullable: false as const,
 		},
 		name: {
diff --git a/src/models/repositories/drive-file.ts b/src/models/repositories/drive-file.ts
index 42a60ff03c..63bd020cbe 100644
--- a/src/models/repositories/drive-file.ts
+++ b/src/models/repositories/drive-file.ts
@@ -234,7 +234,7 @@ export const packedDriveFileSchema = {
 		folder: {
 			type: 'object' as const,
 			optional: true as const, nullable: true as const,
-			ref: 'DriveFolder'
+			ref: 'DriveFolder' as const,
 		},
 		userId: {
 			type: 'string' as const,
@@ -245,7 +245,7 @@ export const packedDriveFileSchema = {
 		user: {
 			type: 'object' as const,
 			optional: true as const, nullable: true as const,
-			ref: 'User'
+			ref: 'User' as const,
 		}
 	},
 };
diff --git a/src/models/repositories/drive-folder.ts b/src/models/repositories/drive-folder.ts
index 4228284f82..bc73018f29 100644
--- a/src/models/repositories/drive-folder.ts
+++ b/src/models/repositories/drive-folder.ts
@@ -87,7 +87,7 @@ export const packedDriveFolderSchema = {
 		parent: {
 			type: 'object' as const,
 			optional: true as const, nullable: true as const,
-			ref: 'DriveFolder'
+			ref: 'DriveFolder' as const,
 		},
 	},
 };
diff --git a/src/models/repositories/following.ts b/src/models/repositories/following.ts
index 3bb120bc4b..24ddd0d676 100644
--- a/src/models/repositories/following.ts
+++ b/src/models/repositories/following.ts
@@ -110,7 +110,7 @@ export const packedFollowingSchema = {
 		followee: {
 			type: 'object' as const,
 			optional: true as const, nullable: false as const,
-			ref: 'User',
+			ref: 'User' as const,
 		},
 		followerId: {
 			type: 'string' as const,
@@ -120,7 +120,7 @@ export const packedFollowingSchema = {
 		follower: {
 			type: 'object' as const,
 			optional: true as const, nullable: false as const,
-			ref: 'User',
+			ref: 'User' as const,
 		},
 	}
 };
diff --git a/src/models/repositories/gallery-post.ts b/src/models/repositories/gallery-post.ts
index 03edb35213..afa22e9edf 100644
--- a/src/models/repositories/gallery-post.ts
+++ b/src/models/repositories/gallery-post.ts
@@ -1,6 +1,6 @@
 import { EntityRepository, Repository } from 'typeorm';
 import { GalleryPost } from '@/models/entities/gallery-post';
-import { SchemaType } from '../../misc/schema';
+import { SchemaType } from '@/misc/schema';
 import { Users, DriveFiles, GalleryLikes } from '../index';
 import { awaitAll } from '@/prelude/await-all';
 import { User } from '@/models/entities/user';
@@ -76,7 +76,7 @@ export const packedGalleryPostSchema = {
 		},
 		user: {
 			type: 'object' as const,
-			ref: 'User',
+			ref: 'User' as const,
 			optional: false as const, nullable: false as const,
 		},
 		fileIds: {
@@ -94,7 +94,7 @@ export const packedGalleryPostSchema = {
 			items: {
 				type: 'object' as const,
 				optional: false as const, nullable: false as const,
-				ref: 'DriveFile'
+				ref: 'DriveFile' as const,
 			}
 		},
 		tags: {
diff --git a/src/models/repositories/messaging-message.ts b/src/models/repositories/messaging-message.ts
index 1a4a8eecc4..f97905af2f 100644
--- a/src/models/repositories/messaging-message.ts
+++ b/src/models/repositories/messaging-message.ts
@@ -67,7 +67,7 @@ export const packedMessagingMessageSchema = {
 		},
 		user: {
 			type: 'object' as const,
-			ref: 'User',
+			ref: 'User' as const,
 			optional: true as const, nullable: false as const,
 		},
 		text: {
@@ -82,7 +82,7 @@ export const packedMessagingMessageSchema = {
 		file: {
 			type: 'object' as const,
 			optional: true as const, nullable: true as const,
-			ref: 'DriveFile',
+			ref: 'DriveFile' as const,
 		},
 		recipientId: {
 			type: 'string' as const,
@@ -92,7 +92,7 @@ export const packedMessagingMessageSchema = {
 		recipient: {
 			type: 'object' as const,
 			optional: true as const, nullable: true as const,
-			ref: 'User'
+			ref: 'User' as const,
 		},
 		groupId: {
 			type: 'string' as const,
@@ -102,7 +102,7 @@ export const packedMessagingMessageSchema = {
 		group: {
 			type: 'object' as const,
 			optional: true as const, nullable: true as const,
-			ref: 'UserGroup'
+			ref: 'UserGroup' as const,
 		},
 		isRead: {
 			type: 'boolean' as const,
diff --git a/src/models/repositories/muting.ts b/src/models/repositories/muting.ts
index e46f4ae448..d957b1792d 100644
--- a/src/models/repositories/muting.ts
+++ b/src/models/repositories/muting.ts
@@ -56,7 +56,7 @@ export const packedMutingSchema = {
 		mutee: {
 			type: 'object' as const,
 			optional: false as const, nullable: false as const,
-			ref: 'User',
+			ref: 'User' as const,
 		},
 	}
 };
diff --git a/src/models/repositories/note-favorite.ts b/src/models/repositories/note-favorite.ts
index 3248c32ded..47586a9116 100644
--- a/src/models/repositories/note-favorite.ts
+++ b/src/models/repositories/note-favorite.ts
@@ -45,7 +45,7 @@ export const packedNoteFavoriteSchema = {
 		note: {
 			type: 'object' as const,
 			optional: false as const, nullable: false as const,
-			ref: 'Note',
+			ref: 'Note' as const,
 		},
 		noteId: {
 			type: 'string' as const,
diff --git a/src/models/repositories/note-reaction.ts b/src/models/repositories/note-reaction.ts
index c349edf182..e73a832109 100644
--- a/src/models/repositories/note-reaction.ts
+++ b/src/models/repositories/note-reaction.ts
@@ -42,7 +42,7 @@ export const packedNoteReactionSchema = {
 		user: {
 			type: 'object' as const,
 			optional: false as const, nullable: false as const,
-			ref: 'User',
+			ref: 'User' as const,
 		},
 		type: {
 			type: 'string' as const,
diff --git a/src/models/repositories/note.ts b/src/models/repositories/note.ts
index 9e0f5e55f0..ac4df222dc 100644
--- a/src/models/repositories/note.ts
+++ b/src/models/repositories/note.ts
@@ -3,7 +3,7 @@ import * as mfm from 'mfm-js';
 import { Note } from '@/models/entities/note';
 import { User } from '@/models/entities/user';
 import { Users, PollVotes, DriveFiles, NoteReactions, Followings, Polls, Channels } from '../index';
-import { SchemaType } from '@/misc/schema';
+import { Schema, SchemaType } from '@/misc/schema';
 import { nyaize } from '@/misc/nyaize';
 import { awaitAll } from '@/prelude/await-all';
 import { convertLegacyReaction, convertLegacyReactions, decodeReaction } from '@/misc/reaction-lib';
@@ -353,7 +353,7 @@ export const packedNoteSchema = {
 		},
 		user: {
 			type: 'object' as const,
-			ref: 'User',
+			ref: 'User' as const,
 			optional: false as const, nullable: false as const,
 		},
 		replyId: {
@@ -371,12 +371,12 @@ export const packedNoteSchema = {
 		reply: {
 			type: 'object' as const,
 			optional: true as const, nullable: true as const,
-			ref: 'Note'
+			ref: 'Note' as const,
 		},
 		renote: {
 			type: 'object' as const,
 			optional: true as const, nullable: true as const,
-			ref: 'Note'
+			ref: 'Note' as const,
 		},
 		viaMobile: {
 			type: 'boolean' as const,
@@ -423,7 +423,7 @@ export const packedNoteSchema = {
 			items: {
 				type: 'object' as const,
 				optional: false as const, nullable: false as const,
-				ref: 'DriveFile'
+				ref: 'DriveFile' as const,
 			}
 		},
 		tags: {
@@ -447,7 +447,7 @@ export const packedNoteSchema = {
 		channel: {
 			type: 'object' as const,
 			optional: true as const, nullable: true as const,
-			ref: 'Channel'
+			ref: 'Channel' as const,
 		},
 		localOnly: {
 			type: 'boolean' as const,
diff --git a/src/models/repositories/notification.ts b/src/models/repositories/notification.ts
index 983fdaf6e8..b7f9e3643c 100644
--- a/src/models/repositories/notification.ts
+++ b/src/models/repositories/notification.ts
@@ -136,7 +136,7 @@ export const packedNotificationSchema = {
 		},
 		user: {
 			type: 'object' as const,
-			ref: 'User',
+			ref: 'User' as const,
 			optional: true as const, nullable: true as const,
 		},
 		userId: {
@@ -159,7 +159,6 @@ export const packedNotificationSchema = {
 		},
 		invitation: {
 			type: 'object' as const,
-			ref: 'Note',
 			optional: true as const, nullable: true as const,
 		},
 		body: {
diff --git a/src/models/repositories/page.ts b/src/models/repositories/page.ts
index 757aaa5a3f..1a61e2c99c 100644
--- a/src/models/repositories/page.ts
+++ b/src/models/repositories/page.ts
@@ -137,7 +137,7 @@ export const packedPageSchema = {
 		},
 		user: {
 			type: 'object' as const,
-			ref: 'User',
+			ref: 'User' as const,
 			optional: false as const, nullable: false as const,
 		},
 	}
diff --git a/src/models/repositories/user.ts b/src/models/repositories/user.ts
index d4bb995ce2..4a6534b557 100644
--- a/src/models/repositories/user.ts
+++ b/src/models/repositories/user.ts
@@ -520,7 +520,7 @@ export const packedUserSchema = {
 			items: {
 				type: 'object' as const,
 				nullable: false as const, optional: false as const,
-				ref: 'Note'
+				ref: 'Note' as const,
 			}
 		},
 		pinnedPageId: {
@@ -530,7 +530,7 @@ export const packedUserSchema = {
 		pinnedPage: {
 			type: 'object' as const,
 			nullable: true as const, optional: false as const,
-			ref: 'Page'
+			ref: 'Page' as const,
 		},
 		twoFactorEnabled: {
 			type: 'boolean' as const,
diff --git a/src/server/api/openapi/schemas.ts b/src/server/api/openapi/schemas.ts
index 5402dc6f48..12fc207c47 100644
--- a/src/server/api/openapi/schemas.ts
+++ b/src/server/api/openapi/schemas.ts
@@ -1,26 +1,4 @@
-import { packedUserSchema } from '@/models/repositories/user';
-import { Schema } from '@/misc/schema';
-import { packedNoteSchema } from '@/models/repositories/note';
-import { packedUserListSchema } from '@/models/repositories/user-list';
-import { packedAppSchema } from '@/models/repositories/app';
-import { packedMessagingMessageSchema } from '@/models/repositories/messaging-message';
-import { packedNotificationSchema } from '@/models/repositories/notification';
-import { packedDriveFileSchema } from '@/models/repositories/drive-file';
-import { packedDriveFolderSchema } from '@/models/repositories/drive-folder';
-import { packedFollowingSchema } from '@/models/repositories/following';
-import { packedMutingSchema } from '@/models/repositories/muting';
-import { packedBlockingSchema } from '@/models/repositories/blocking';
-import { packedNoteReactionSchema } from '@/models/repositories/note-reaction';
-import { packedHashtagSchema } from '@/models/repositories/hashtag';
-import { packedPageSchema } from '@/models/repositories/page';
-import { packedUserGroupSchema } from '@/models/repositories/user-group';
-import { packedNoteFavoriteSchema } from '@/models/repositories/note-favorite';
-import { packedChannelSchema } from '@/models/repositories/channel';
-import { packedAntennaSchema } from '@/models/repositories/antenna';
-import { packedClipSchema } from '@/models/repositories/clip';
-import { packedFederationInstanceSchema } from '@/models/repositories/federation-instance';
-import { packedQueueCountSchema } from '@/models/repositories/queue';
-import { packedGalleryPostSchema } from '@/models/repositories/gallery-post';
+import { refs, Schema } from '@/misc/schema';
 
 export function convertSchemaToOpenApiSchema(schema: Schema) {
 	const res: any = schema;
@@ -72,26 +50,7 @@ export const schemas = {
 		required: ['error']
 	},
 
-	User: convertSchemaToOpenApiSchema(packedUserSchema),
-	UserList: convertSchemaToOpenApiSchema(packedUserListSchema),
-	UserGroup: convertSchemaToOpenApiSchema(packedUserGroupSchema),
-	App: convertSchemaToOpenApiSchema(packedAppSchema),
-	MessagingMessage: convertSchemaToOpenApiSchema(packedMessagingMessageSchema),
-	Note: convertSchemaToOpenApiSchema(packedNoteSchema),
-	NoteReaction: convertSchemaToOpenApiSchema(packedNoteReactionSchema),
-	NoteFavorite: convertSchemaToOpenApiSchema(packedNoteFavoriteSchema),
-	Notification: convertSchemaToOpenApiSchema(packedNotificationSchema),
-	DriveFile: convertSchemaToOpenApiSchema(packedDriveFileSchema),
-	DriveFolder: convertSchemaToOpenApiSchema(packedDriveFolderSchema),
-	Following: convertSchemaToOpenApiSchema(packedFollowingSchema),
-	Muting: convertSchemaToOpenApiSchema(packedMutingSchema),
-	Blocking: convertSchemaToOpenApiSchema(packedBlockingSchema),
-	Hashtag: convertSchemaToOpenApiSchema(packedHashtagSchema),
-	Page: convertSchemaToOpenApiSchema(packedPageSchema),
-	Channel: convertSchemaToOpenApiSchema(packedChannelSchema),
-	QueueCount: convertSchemaToOpenApiSchema(packedQueueCountSchema),
-	Antenna: convertSchemaToOpenApiSchema(packedAntennaSchema),
-	Clip: convertSchemaToOpenApiSchema(packedClipSchema),
-	FederationInstance: convertSchemaToOpenApiSchema(packedFederationInstanceSchema),
-	GalleryPost: convertSchemaToOpenApiSchema(packedGalleryPostSchema),
+	...Object.fromEntries(
+		Object.entries(refs).map(([key, schema]) => [key, convertSchemaToOpenApiSchema(schema)])
+	),
 };

From 93fd76f84127c6e0523d6eb731c2a9fcf2f4c349 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 Sep 2021 03:32:52 +0900
Subject: [PATCH 16/91] remove no longer needed "as PackedUser/PackedNote"

---
 src/models/repositories/note.ts                   | 2 +-
 src/server/api/stream/channels/global-timeline.ts | 2 +-
 src/server/api/stream/channels/home-timeline.ts   | 2 +-
 src/server/api/stream/channels/hybrid-timeline.ts | 5 ++---
 src/server/api/stream/channels/local-timeline.ts  | 5 ++---
 src/server/api/stream/index.ts                    | 4 ++--
 6 files changed, 9 insertions(+), 11 deletions(-)

diff --git a/src/models/repositories/note.ts b/src/models/repositories/note.ts
index ac4df222dc..52f841b526 100644
--- a/src/models/repositories/note.ts
+++ b/src/models/repositories/note.ts
@@ -95,7 +95,7 @@ export class NoteRepository extends Repository<Note> {
 				hide = true;
 			} else if (meId === packedNote.userId) {
 				hide = false;
-			} else if (packedNote.reply && (meId === (packedNote.reply as PackedNote).userId)) {
+			} else if (packedNote.reply && (meId === packedNote.reply.userId)) {
 				// 自分の投稿に対するリプライ
 				hide = false;
 			} else if (packedNote.mentions && packedNote.mentions.some(id => meId === id)) {
diff --git a/src/server/api/stream/channels/global-timeline.ts b/src/server/api/stream/channels/global-timeline.ts
index 2cb138966f..384ed61409 100644
--- a/src/server/api/stream/channels/global-timeline.ts
+++ b/src/server/api/stream/channels/global-timeline.ts
@@ -43,7 +43,7 @@ export default class extends Channel {
 
 		// 関係ない返信は除外
 		if (note.reply) {
-			const reply = note.reply as PackedNote;
+			const reply = note.reply;
 			// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
 			if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return;
 		}
diff --git a/src/server/api/stream/channels/home-timeline.ts b/src/server/api/stream/channels/home-timeline.ts
index c7a9728741..0e21ab552e 100644
--- a/src/server/api/stream/channels/home-timeline.ts
+++ b/src/server/api/stream/channels/home-timeline.ts
@@ -51,7 +51,7 @@ export default class extends Channel {
 
 		// 関係ない返信は除外
 		if (note.reply) {
-			const reply = note.reply as PackedNote;
+			const reply = note.reply;
 			// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
 			if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return;
 		}
diff --git a/src/server/api/stream/channels/hybrid-timeline.ts b/src/server/api/stream/channels/hybrid-timeline.ts
index 5c454764ec..0b28ff616b 100644
--- a/src/server/api/stream/channels/hybrid-timeline.ts
+++ b/src/server/api/stream/channels/hybrid-timeline.ts
@@ -4,7 +4,6 @@ import Channel from '../channel';
 import { fetchMeta } from '@/misc/fetch-meta';
 import { Notes } from '@/models/index';
 import { PackedNote } from '@/models/repositories/note';
-import { PackedUser } from '@/models/repositories/user';
 import { checkWordMute } from '@/misc/check-word-mute';
 import { isBlockerUserRelated } from '@/misc/is-blocker-user-related';
 
@@ -31,7 +30,7 @@ export default class extends Channel {
 		if (!(
 			(note.channelId == null && this.user!.id === note.userId) ||
 			(note.channelId == null && this.following.has(note.userId)) ||
-			(note.channelId == null && ((note.user as PackedUser).host == null && note.visibility === 'public')) ||
+			(note.channelId == null && (note.user.host == null && note.visibility === 'public')) ||
 			(note.channelId != null && this.followingChannels.has(note.channelId))
 		)) return;
 
@@ -60,7 +59,7 @@ export default class extends Channel {
 
 		// 関係ない返信は除外
 		if (note.reply) {
-			const reply = note.reply as PackedNote;
+			const reply = note.reply;
 			// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
 			if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return;
 		}
diff --git a/src/server/api/stream/channels/local-timeline.ts b/src/server/api/stream/channels/local-timeline.ts
index 4bf0d02ed3..20061410c4 100644
--- a/src/server/api/stream/channels/local-timeline.ts
+++ b/src/server/api/stream/channels/local-timeline.ts
@@ -4,7 +4,6 @@ import Channel from '../channel';
 import { fetchMeta } from '@/misc/fetch-meta';
 import { Notes } from '@/models/index';
 import { PackedNote } from '@/models/repositories/note';
-import { PackedUser } from '@/models/repositories/user';
 import { checkWordMute } from '@/misc/check-word-mute';
 import { isBlockerUserRelated } from '@/misc/is-blocker-user-related';
 
@@ -26,7 +25,7 @@ export default class extends Channel {
 
 	@autobind
 	private async onNote(note: PackedNote) {
-		if ((note.user as PackedUser).host !== null) return;
+		if (note.user.host !== null) return;
 		if (note.visibility !== 'public') return;
 		if (note.channelId != null && !this.followingChannels.has(note.channelId)) return;
 
@@ -45,7 +44,7 @@ export default class extends Channel {
 
 		// 関係ない返信は除外
 		if (note.reply) {
-			const reply = note.reply as PackedNote;
+			const reply = note.reply;
 			// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
 			if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return;
 		}
diff --git a/src/server/api/stream/index.ts b/src/server/api/stream/index.ts
index 469f28f11c..f83bc9331e 100644
--- a/src/server/api/stream/index.ts
+++ b/src/server/api/stream/index.ts
@@ -165,8 +165,8 @@ export default class Connection {
 		};
 
 		add(note);
-		if (note.reply) add(note.reply as PackedNote);
-		if (note.renote) add(note.renote as PackedNote);
+		if (note.reply) add(note.reply);
+		if (note.renote) add(note.renote);
 	}
 
 	@autobind

From f89a326d7f56eacfbfd77062d7250b008f657279 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 Sep 2021 03:34:21 +0900
Subject: [PATCH 17/91] clean up

---
 src/models/repositories/note.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/models/repositories/note.ts b/src/models/repositories/note.ts
index 52f841b526..77fedd791f 100644
--- a/src/models/repositories/note.ts
+++ b/src/models/repositories/note.ts
@@ -3,7 +3,7 @@ import * as mfm from 'mfm-js';
 import { Note } from '@/models/entities/note';
 import { User } from '@/models/entities/user';
 import { Users, PollVotes, DriveFiles, NoteReactions, Followings, Polls, Channels } from '../index';
-import { Schema, SchemaType } from '@/misc/schema';
+import { SchemaType } from '@/misc/schema';
 import { nyaize } from '@/misc/nyaize';
 import { awaitAll } from '@/prelude/await-all';
 import { convertLegacyReaction, convertLegacyReactions, decodeReaction } from '@/misc/reaction-lib';

From abbeb9a0718759c8d92bd2ec4db51010ee64767f Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 Sep 2021 12:59:51 +0900
Subject: [PATCH 18/91] add simple-schema

---
 src/misc/schema.ts          | 14 +++-----------
 src/misc/simple-schema.ts   | 15 +++++++++++++++
 src/server/api/endpoints.ts |  4 ++--
 src/services/chart/core.ts  | 14 +++++++-------
 test/utils.ts               |  2 +-
 5 files changed, 28 insertions(+), 21 deletions(-)
 create mode 100644 src/misc/simple-schema.ts

diff --git a/src/misc/schema.ts b/src/misc/schema.ts
index c4ca80249d..f6fb128751 100644
--- a/src/misc/schema.ts
+++ b/src/misc/schema.ts
@@ -1,3 +1,4 @@
+import { SimpleObj, SimpleSchema } from './simple-schema';
 import { packedUserSchema } from '@/models/repositories/user';
 import { packedNoteSchema } from '@/models/repositories/note';
 import { packedUserListSchema } from '@/models/repositories/user-list';
@@ -46,18 +47,9 @@ export const refs = {
 	GalleryPost: packedGalleryPostSchema,
 };
 
-export type Schema = {
-	type: 'boolean' | 'number' | 'string' | 'array' | 'object' | 'any';
-	nullable: boolean;
-	optional: boolean;
-	items?: Schema;
+export interface Schema extends SimpleSchema {
 	properties?: Obj;
-	description?: string;
-	example?: any;
-	format?: string;
 	ref?: keyof typeof refs;
-	enum?: string[];
-	default?: boolean | null;
 };
 
 type NonUndefinedPropertyNames<T extends Obj> = {
@@ -71,7 +63,7 @@ type UndefinedPropertyNames<T extends Obj> = {
 type OnlyRequired<T extends Obj> = Pick<T, NonUndefinedPropertyNames<T>>;
 type OnlyOptional<T extends Obj> = Pick<T, UndefinedPropertyNames<T>>;
 
-export type Obj = { [key: string]: Schema };
+export interface Obj extends SimpleObj { [key: string]: Schema };
 
 export type ObjType<s extends Obj> =
 	{ [P in keyof OnlyOptional<s>]?: SchemaType<s[P]> } &
diff --git a/src/misc/simple-schema.ts b/src/misc/simple-schema.ts
new file mode 100644
index 0000000000..83590edbf2
--- /dev/null
+++ b/src/misc/simple-schema.ts
@@ -0,0 +1,15 @@
+export interface SimpleSchema {
+	type: 'boolean' | 'number' | 'string' | 'array' | 'object' | 'any';
+	nullable: boolean;
+	optional: boolean;
+	items?: SimpleSchema;
+	properties?: SimpleObj;
+	description?: string;
+	example?: any;
+	format?: string;
+	ref?: string;
+	enum?: string[];
+	default?: boolean | null;
+};
+
+export type SimpleObj = { [key: string]: SimpleSchema };
diff --git a/src/server/api/endpoints.ts b/src/server/api/endpoints.ts
index 640b14ed6a..6d9d2b0782 100644
--- a/src/server/api/endpoints.ts
+++ b/src/server/api/endpoints.ts
@@ -3,7 +3,7 @@ import { dirname } from 'path';
 import { Context } from 'cafy';
 import * as path from 'path';
 import * as glob from 'glob';
-import { Schema } from '@/misc/schema';
+import { SimpleSchema } from '@/misc/simple-schema';
 
 //const _filename = fileURLToPath(import.meta.url);
 const _filename = __filename;
@@ -34,7 +34,7 @@ export interface IEndpointMeta {
 		};
 	};
 
-	res?: Schema;
+	res?: SimpleSchema;
 
 	/**
 	 * このエンドポイントにリクエストするのにユーザー情報が必須か否か
diff --git a/src/services/chart/core.ts b/src/services/chart/core.ts
index eee7d20efb..c0d3280c2b 100644
--- a/src/services/chart/core.ts
+++ b/src/services/chart/core.ts
@@ -7,7 +7,7 @@
 import * as nestedProperty from 'nested-property';
 import autobind from 'autobind-decorator';
 import Logger from '../logger';
-import { Schema } from '@/misc/schema';
+import { SimpleSchema } from '@/misc/simple-schema';
 import { EntitySchema, getRepository, Repository, LessThan, Between } from 'typeorm';
 import { dateUTC, isTimeSame, isTimeBefore, subtractTime, addTime } from '@/prelude/time';
 import { getChartInsertLock } from '@/misc/app-lock';
@@ -56,7 +56,7 @@ export default abstract class Chart<T extends Record<string, any>> {
 		diff: DeepPartial<T>;
 		group: string | null;
 	}[] = [];
-	public schema: Schema;
+	public schema: SimpleSchema;
 	protected repository: Repository<Log>;
 
 	protected abstract genNewLog(latest: T): DeepPartial<T>;
@@ -69,7 +69,7 @@ export default abstract class Chart<T extends Record<string, any>> {
 	protected abstract fetchActual(group: string | null): Promise<DeepPartial<T>>;
 
 	@autobind
-	private static convertSchemaToFlatColumnDefinitions(schema: Schema) {
+	private static convertSchemaToFlatColumnDefinitions(schema: SimpleSchema) {
 		const columns = {} as any;
 		const flatColumns = (x: Obj, path?: string) => {
 			for (const [k, v] of Object.entries(x)) {
@@ -181,7 +181,7 @@ export default abstract class Chart<T extends Record<string, any>> {
 	}
 
 	@autobind
-	public static schemaToEntity(name: string, schema: Schema): EntitySchema {
+	public static schemaToEntity(name: string, schema: SimpleSchema): EntitySchema {
 		return new EntitySchema({
 			name: `__chart__${camelToSnake(name)}`,
 			columns: {
@@ -211,7 +211,7 @@ export default abstract class Chart<T extends Record<string, any>> {
 		});
 	}
 
-	constructor(name: string, schema: Schema, grouped = false) {
+	constructor(name: string, schema: SimpleSchema, grouped = false) {
 		this.name = name;
 		this.schema = schema;
 		const entity = Chart.schemaToEntity(name, schema);
@@ -546,8 +546,8 @@ export default abstract class Chart<T extends Record<string, any>> {
 	}
 }
 
-export function convertLog(logSchema: Schema): Schema {
-	const v: Schema = JSON.parse(JSON.stringify(logSchema)); // copy
+export function convertLog(logSchema: SimpleSchema): SimpleSchema {
+	const v: SimpleSchema = JSON.parse(JSON.stringify(logSchema)); // copy
 	if (v.type === 'number') {
 		v.type = 'array';
 		v.items = {
diff --git a/test/utils.ts b/test/utils.ts
index 1a0c54463d..253c410bf0 100644
--- a/test/utils.ts
+++ b/test/utils.ts
@@ -158,7 +158,7 @@ export async function initTestDb(justBorrow = false, initEntities?: any[]) {
 		await conn.close();
 	} catch (e) {}
 
-	return await createConnection({
+	return createConnection({
 		type: 'postgres',
 		host: config.db.host,
 		port: config.db.port,

From 02dbdc54c310be173550709a4ae43b885800c92d Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 Sep 2021 13:08:51 +0900
Subject: [PATCH 19/91] fix lint

---
 src/misc/schema.ts        | 4 ++--
 src/misc/simple-schema.ts | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/misc/schema.ts b/src/misc/schema.ts
index f6fb128751..90d4c3b715 100644
--- a/src/misc/schema.ts
+++ b/src/misc/schema.ts
@@ -50,7 +50,7 @@ export const refs = {
 export interface Schema extends SimpleSchema {
 	properties?: Obj;
 	ref?: keyof typeof refs;
-};
+}
 
 type NonUndefinedPropertyNames<T extends Obj> = {
 	[K in keyof T]: T[K]['optional'] extends true ? never : K
@@ -63,7 +63,7 @@ type UndefinedPropertyNames<T extends Obj> = {
 type OnlyRequired<T extends Obj> = Pick<T, NonUndefinedPropertyNames<T>>;
 type OnlyOptional<T extends Obj> = Pick<T, UndefinedPropertyNames<T>>;
 
-export interface Obj extends SimpleObj { [key: string]: Schema };
+export interface Obj extends SimpleObj { [key: string]: Schema; }
 
 export type ObjType<s extends Obj> =
 	{ [P in keyof OnlyOptional<s>]?: SchemaType<s[P]> } &
diff --git a/src/misc/simple-schema.ts b/src/misc/simple-schema.ts
index 83590edbf2..abbb348e24 100644
--- a/src/misc/simple-schema.ts
+++ b/src/misc/simple-schema.ts
@@ -10,6 +10,6 @@ export interface SimpleSchema {
 	ref?: string;
 	enum?: string[];
 	default?: boolean | null;
-};
+}
 
-export type SimpleObj = { [key: string]: SimpleSchema };
+export interface SimpleObj { [key: string]: SimpleSchema; }

From ca82538e10c34cc7266fd2d2dc8795a439f33e9a Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 Sep 2021 17:16:30 +0900
Subject: [PATCH 20/91] fix lint

---
 src/server/api/stream/types.ts | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/server/api/stream/types.ts b/src/server/api/stream/types.ts
index 549af96fa8..e0c76306bd 100644
--- a/src/server/api/stream/types.ts
+++ b/src/server/api/stream/types.ts
@@ -19,7 +19,7 @@ type EventUnionFromDictionary<
 type EventUnionFromMkJSTypes<
 	T extends { [key: string]: ((payload: any) => void) | (() => void) },
 	U = { [K in keyof T]: { type: K; body: Payload<T[K]>} }
-> = U[keyof U]
+> = U[keyof U];
 
 export type BroadcastStream = EventUnionFromMkJSTypes<StreamTypes.BroadcasrEvents>;
 
@@ -33,11 +33,11 @@ export interface UserEventTypes {
 	follow: PackedUser;
 	unfollow: PackedUser;
 	userAdded: PackedUser;
-};
+}
 export type UserEventName = `user:${User['id']}`;
 export type UserEvents = EventUnionFromDictionary<UserEventTypes>;
 
-export interface mainStreamTypes {
+export interface MainStreamTypes {
 	notification: PackedNotification;
 	mention: PackedNote;
 	reply: PackedNote;
@@ -62,9 +62,9 @@ export interface mainStreamTypes {
 	readAllChannels: never;
 	unreadChannel: never;
 	myTokenRegenerated: never;
-};
+}
 export type mainStreamName = `mainStream:${User['id']}`;
-export type mainStreams = EventUnionFromDictionary<mainStreamTypes>;
+export type mainStreams = EventUnionFromDictionary<MainStreamTypes>;
 
 interface StreamEvents {
 	'broadcast': (e: BroadcastStream) => void;

From dd175a8e6ee0093d2711bd1bcde9ea7f5fc099fc Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 Sep 2021 17:56:17 +0900
Subject: [PATCH 21/91] wip

---
 src/server/api/stream/index.ts |  4 +-
 src/server/api/stream/types.ts | 95 ++++++++++++++++++++++++++++++----
 2 files changed, 87 insertions(+), 12 deletions(-)

diff --git a/src/server/api/stream/index.ts b/src/server/api/stream/index.ts
index 0101e6e079..b2cecb4470 100644
--- a/src/server/api/stream/index.ts
+++ b/src/server/api/stream/index.ts
@@ -15,7 +15,7 @@ import { UserProfile } from '@/models/entities/user-profile';
 import { publishChannelStream, publishGroupMessagingStream, publishMessagingStream } from '@/services/stream';
 import { UserGroup } from '@/models/entities/user-group';
 import { PackedNote } from '@/models/repositories/note';
-import { StreamEventEmitter, UserEvents } from './types';
+import { StreamEventEmitter, UserStreams } from './types';
 
 /**
  * Main stream connection
@@ -65,7 +65,7 @@ export default class Connection {
 	}
 
 	@autobind
-	private onUserEvent(ev: UserEvents) { // { type, body }と展開すると型も展開されてしまう
+	private onUserEvent(ev: UserStreams) { // { type, body }と展開すると型も展開されてしまう
 		switch (ev.type) {
 			case 'follow':
 				this.following.add(ev.body.id);
diff --git a/src/server/api/stream/types.ts b/src/server/api/stream/types.ts
index e0c76306bd..3803e6dcea 100644
--- a/src/server/api/stream/types.ts
+++ b/src/server/api/stream/types.ts
@@ -7,23 +7,46 @@ import { UserProfile } from '@/models/entities/user-profile';
 import { PackedUser } from '@/models/repositories/user';
 import { PackedNotification } from '@/models/repositories/notification';
 import { PackedNote } from '@/models/repositories/note';
+import { Antenna } from '@/models/entities/antenna';
+import { DriveFile } from '@/models/entities/drive-file';
+import { PackedDriveFile } from '@/models/repositories/drive-file';
+import { PackedDriveFolder } from '@/models/repositories/drive-folder';
+import { DriveFolder } from '@/models/entities/drive-folder';
+import { Note } from '@/models/entities/note';
+import { Emoji } from '@/models/entities/emoji';
 
-type Payload<T extends (payload: any) => void> = T extends (payload: infer P) => void ? P : never;
-
+// 辞書(interface or type)から{ type, body }ユニオンを定義
 // https://stackoverflow.com/questions/49311989/can-i-infer-the-type-of-a-value-using-extends-keyof-type
 type EventUnionFromDictionary<
 	T extends object,
 	U = { [K in keyof T]: { type: K; body: T[K]} }
 > = U[keyof U];
 
+// (payload: P) => void からPを取り出す
+type Payload<T extends (payload: any) => void> = T extends (payload: infer P) => void ? P : never;
+
+// misskey.jsのstreaming.typesの辞書から{ type, body }ユニオンを定義
 type EventUnionFromMkJSTypes<
 	T extends { [key: string]: ((payload: any) => void) | (() => void) },
 	U = { [K in keyof T]: { type: K; body: Payload<T[K]>} }
 > = U[keyof U];
 
-export type BroadcastStream = EventUnionFromMkJSTypes<StreamTypes.BroadcasrEvents>;
+//#region Stream type-body definitions
 
-export interface UserEventTypes {
+// internal
+export interface InternalStreamTypes {
+	antennaCreated: Antenna;
+	antennaDeleted: Antenna;
+	antennaUpdated: Antenna;
+}
+export type InternalStreams = EventUnionFromDictionary<InternalStreamTypes>;
+
+// broadcast
+export type BroadcastStreams = EventUnionFromMkJSTypes<StreamTypes.BroadcasrEvents>;
+
+// user
+export type UserEventName = `user:${User['id']}`;
+export interface UserStreamTypes {
 	terminate: {};
 	followChannel: Channel;
 	unfollowChannel: Channel;
@@ -34,9 +57,10 @@ export interface UserEventTypes {
 	unfollow: PackedUser;
 	userAdded: PackedUser;
 }
-export type UserEventName = `user:${User['id']}`;
-export type UserEvents = EventUnionFromDictionary<UserEventTypes>;
+export type UserStreams = EventUnionFromDictionary<UserStreamTypes>;
 
+// main
+export type mainStreamName = `mainStream:${User['id']}`;
 export interface MainStreamTypes {
 	notification: PackedNotification;
 	mention: PackedNote;
@@ -63,17 +87,68 @@ export interface MainStreamTypes {
 	unreadChannel: never;
 	myTokenRegenerated: never;
 }
-export type mainStreamName = `mainStream:${User['id']}`;
 export type mainStreams = EventUnionFromDictionary<MainStreamTypes>;
 
+// drive
+export type driveStreamName = `driveStream:${User['id']}`;
+export interface DriveStreamTypes {
+	fileCreated: PackedDriveFile;
+	fileDeleted: DriveFile['id'];
+	fileUpdated: PackedDriveFile;
+	folderCreated: PackedDriveFolder;
+	folderDeleted: DriveFolder['id'];
+	folderUpdated: PackedDriveFolder;
+}
+export type driveStreams= EventUnionFromDictionary<DriveStreamTypes>;
+
+// note
+export type noteStreamName = `noteStream:${Note['id']}`;
+export interface NoteStreamTypes {
+	pollVoted: {
+		id: Note['id'];
+		body: {
+			choice: number;
+			userId: User['id'];
+		};
+	};
+	deleted: {
+		id: Note['id'];
+		body: {
+			deletedAt: Date;
+		};
+	};
+	reacted: {
+		id: Note['id'];
+		body: {
+			reaction: string;
+			emoji?: Emoji;
+			userId: User['id'];
+		};
+	};
+	unreacted: {
+		id: Note['id'];
+		body: {
+			reaction: string;
+			userId: User['id'];
+		}
+	};
+}
+export type noteStreams = EventUnionFromDictionary<NoteStreamTypes>;
+
+//#endregion
+
+//#region API event definitions
 interface StreamEvents {
-	'broadcast': (e: BroadcastStream) => void;
+	'broadcast': (e: BroadcastStreams) => void;
+	'internal': (e: InternalStreams) => void;
 }
 
 interface AuthenticatedStreamEvents {
-	[key: UserEventName]: (e: UserEvents) => void;
+	[key: UserEventName]: (e: UserStreams) => void;
 	[key: mainStreamName]: (e: mainStreams) => void;
-	[key: `driveStream:${User['id']}`]: (e: { type: string; body: any }) => void;
+	[key: driveStreamName]: (e: driveStreams) => void;
+	[key: noteStreamName]: (e: noteStreams) => void;
 }
 
 export type StreamEventEmitter = Emitter<EventEmitter, AuthenticatedStreamEvents & StreamEvents>;
+//#endregion

From 70740c5e6d516dd5dceae35b68b4ea8189fb4d4e Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 Sep 2021 18:29:23 +0900
Subject: [PATCH 22/91] wip!

---
 src/server/api/stream/types.ts | 67 +++++++++++++++++-----------------
 1 file changed, 34 insertions(+), 33 deletions(-)

diff --git a/src/server/api/stream/types.ts b/src/server/api/stream/types.ts
index 3803e6dcea..e35a810ac7 100644
--- a/src/server/api/stream/types.ts
+++ b/src/server/api/stream/types.ts
@@ -32,20 +32,12 @@ type EventUnionFromMkJSTypes<
 > = U[keyof U];
 
 //#region Stream type-body definitions
-
-// internal
 export interface InternalStreamTypes {
 	antennaCreated: Antenna;
 	antennaDeleted: Antenna;
 	antennaUpdated: Antenna;
 }
-export type InternalStreams = EventUnionFromDictionary<InternalStreamTypes>;
 
-// broadcast
-export type BroadcastStreams = EventUnionFromMkJSTypes<StreamTypes.BroadcasrEvents>;
-
-// user
-export type UserEventName = `user:${User['id']}`;
 export interface UserStreamTypes {
 	terminate: {};
 	followChannel: Channel;
@@ -57,10 +49,7 @@ export interface UserStreamTypes {
 	unfollow: PackedUser;
 	userAdded: PackedUser;
 }
-export type UserStreams = EventUnionFromDictionary<UserStreamTypes>;
 
-// main
-export type mainStreamName = `mainStream:${User['id']}`;
 export interface MainStreamTypes {
 	notification: PackedNotification;
 	mention: PackedNote;
@@ -87,10 +76,7 @@ export interface MainStreamTypes {
 	unreadChannel: never;
 	myTokenRegenerated: never;
 }
-export type mainStreams = EventUnionFromDictionary<MainStreamTypes>;
 
-// drive
-export type driveStreamName = `driveStream:${User['id']}`;
 export interface DriveStreamTypes {
 	fileCreated: PackedDriveFile;
 	fileDeleted: DriveFile['id'];
@@ -99,10 +85,7 @@ export interface DriveStreamTypes {
 	folderDeleted: DriveFolder['id'];
 	folderUpdated: PackedDriveFolder;
 }
-export type driveStreams= EventUnionFromDictionary<DriveStreamTypes>;
 
-// note
-export type noteStreamName = `noteStream:${Note['id']}`;
 export interface NoteStreamTypes {
 	pollVoted: {
 		id: Note['id'];
@@ -133,22 +116,40 @@ export interface NoteStreamTypes {
 		}
 	};
 }
-export type noteStreams = EventUnionFromDictionary<NoteStreamTypes>;
+
+//#endregion
+//#region 名前とメッセージのペアを中間生成
+interface StreamMessages {
+	internal: {
+		name: 'internal';
+		spec: EventUnionFromDictionary<InternalStreamTypes>;
+	};
+	broadcast: {
+		name: 'bloadcast';
+		spec: EventUnionFromMkJSTypes<StreamTypes.BroadcasrEvents>;
+	};
+	user: {
+		name: `user:${User['id']}`;
+		spec: EventUnionFromDictionary<UserStreamTypes>;
+	};
+	main: {
+		name: `mainStream:${User['id']}`;
+		spec: EventUnionFromDictionary<MainStreamTypes>;
+	};
+	drive: {
+		name: `driveStream:${User['id']}`;
+		spec: EventUnionFromDictionary<DriveStreamTypes>;
+	};
+	note: {
+		name: `noteStream:${Note['id']}`;
+		spec: EventUnionFromDictionary<NoteStreamTypes>;
+	}
+}
 
 //#endregion
 
-//#region API event definitions
-interface StreamEvents {
-	'broadcast': (e: BroadcastStreams) => void;
-	'internal': (e: InternalStreams) => void;
-}
-
-interface AuthenticatedStreamEvents {
-	[key: UserEventName]: (e: UserStreams) => void;
-	[key: mainStreamName]: (e: mainStreams) => void;
-	[key: driveStreamName]: (e: driveStreams) => void;
-	[key: noteStreamName]: (e: noteStreams) => void;
-}
-
-export type StreamEventEmitter = Emitter<EventEmitter, AuthenticatedStreamEvents & StreamEvents>;
-//#endregion
+// API event definitions
+type Events<T extends keyof S> = {
+	[x in S[T]['name']]: (e: S[T]['spec']) => void
+};
+export type StreamEventEmitter = Emitter<EventEmitter, Events<StreamMessages>>;

From ac3dbcb11444f6707b3062f3f9a0491c250e451d Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 Sep 2021 20:20:39 +0900
Subject: [PATCH 23/91] wip

---
 src/server/api/stream/types.ts | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/server/api/stream/types.ts b/src/server/api/stream/types.ts
index e35a810ac7..32ba414b30 100644
--- a/src/server/api/stream/types.ts
+++ b/src/server/api/stream/types.ts
@@ -149,7 +149,5 @@ interface StreamMessages {
 //#endregion
 
 // API event definitions
-type Events<T extends keyof S> = {
-	[x in S[T]['name']]: (e: S[T]['spec']) => void
-};
-export type StreamEventEmitter = Emitter<EventEmitter, Events<StreamMessages>>;
+type EventsGenerater<K extends keyof StreamMessages> = { [key in StreamMessages[K]['name']]: (e: StreamMessages[K]['spec']) => void };
+export type StreamEventEmitter = Emitter<EventEmitter, EventsGenerater<keyof StreamMessages>>;

From 35007055852f28da86cc4cbebdc97bd0373f2561 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 Sep 2021 20:21:05 +0900
Subject: [PATCH 24/91] fix

---
 src/server/api/stream/types.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/server/api/stream/types.ts b/src/server/api/stream/types.ts
index 32ba414b30..18eba58538 100644
--- a/src/server/api/stream/types.ts
+++ b/src/server/api/stream/types.ts
@@ -125,7 +125,7 @@ interface StreamMessages {
 		spec: EventUnionFromDictionary<InternalStreamTypes>;
 	};
 	broadcast: {
-		name: 'bloadcast';
+		name: 'broadcast';
 		spec: EventUnionFromMkJSTypes<StreamTypes.BroadcasrEvents>;
 	};
 	user: {

From f4652ec4ac6e1ee3a9daa85586a7685c3907381e Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 Sep 2021 22:12:07 +0900
Subject: [PATCH 25/91] wip

---
 src/models/repositories/emoji.ts              |   2 +
 src/models/repositories/games/reversi/game.ts |   2 +
 .../repositories/games/reversi/matching.ts    |   2 +
 .../api/common/read-messaging-message.ts      |   2 +-
 src/server/api/endpoints/antennas/update.ts   |   2 +-
 src/server/api/stream/channels/channel.ts     |   2 +-
 src/server/api/stream/types.ts                | 154 ++++++++++++++++--
 src/services/stream.ts                        |   9 +-
 8 files changed, 158 insertions(+), 17 deletions(-)

diff --git a/src/models/repositories/emoji.ts b/src/models/repositories/emoji.ts
index c3d7184ec9..1bbe6b8215 100644
--- a/src/models/repositories/emoji.ts
+++ b/src/models/repositories/emoji.ts
@@ -1,6 +1,8 @@
 import { EntityRepository, Repository } from 'typeorm';
 import { Emoji } from '@/models/entities/emoji';
 
+export type PackedEmoji = FIXME;
+
 @EntityRepository(Emoji)
 export class EmojiRepository extends Repository<Emoji> {
 	public async pack(
diff --git a/src/models/repositories/games/reversi/game.ts b/src/models/repositories/games/reversi/game.ts
index dc91ad51b8..eb2561be91 100644
--- a/src/models/repositories/games/reversi/game.ts
+++ b/src/models/repositories/games/reversi/game.ts
@@ -3,6 +3,8 @@ import { EntityRepository, Repository } from 'typeorm';
 import { Users } from '../../../index';
 import { ReversiGame } from '@/models/entities/games/reversi/game';
 
+export type PackedReversiGame = FIXME;
+
 @EntityRepository(ReversiGame)
 export class ReversiGameRepository extends Repository<ReversiGame> {
 	public async pack(
diff --git a/src/models/repositories/games/reversi/matching.ts b/src/models/repositories/games/reversi/matching.ts
index 148221dee5..91615091ac 100644
--- a/src/models/repositories/games/reversi/matching.ts
+++ b/src/models/repositories/games/reversi/matching.ts
@@ -4,6 +4,8 @@ import { Users } from '../../../index';
 import { awaitAll } from '@/prelude/await-all';
 import { User } from '@/models/entities/user';
 
+export type PackedReversiMatching = FIXME;
+
 @EntityRepository(ReversiMatching)
 export class ReversiMatchingRepository extends Repository<ReversiMatching> {
 	public async pack(
diff --git a/src/server/api/common/read-messaging-message.ts b/src/server/api/common/read-messaging-message.ts
index 1dce76d2a9..33f41b2770 100644
--- a/src/server/api/common/read-messaging-message.ts
+++ b/src/server/api/common/read-messaging-message.ts
@@ -77,7 +77,7 @@ export async function readGroupMessagingMessage(
 		id: In(messageIds)
 	});
 
-	const reads = [];
+	const reads: MessagingMessage['id'][] = [];
 
 	for (const message of messages) {
 		if (message.userId === userId) continue;
diff --git a/src/server/api/endpoints/antennas/update.ts b/src/server/api/endpoints/antennas/update.ts
index ff13e89bcc..d69b4feee6 100644
--- a/src/server/api/endpoints/antennas/update.ts
+++ b/src/server/api/endpoints/antennas/update.ts
@@ -137,7 +137,7 @@ export default define(meta, async (ps, user) => {
 		notify: ps.notify,
 	});
 
-	publishInternalEvent('antennaUpdated', Antennas.findOneOrFail(antenna.id));
+	publishInternalEvent('antennaUpdated', await Antennas.findOneOrFail(antenna.id));
 
 	return await Antennas.pack(antenna.id);
 });
diff --git a/src/server/api/stream/channels/channel.ts b/src/server/api/stream/channels/channel.ts
index e6a9a6c696..a5692563f3 100644
--- a/src/server/api/stream/channels/channel.ts
+++ b/src/server/api/stream/channels/channel.ts
@@ -19,7 +19,7 @@ export default class extends Channel {
 		this.channelId = params.channelId as string;
 
 		// Subscribe stream
-		this.subscriber.on('notesStream', this.onNote);
+		this.subscriber.on('notesStream', e => this.onNote(e));
 		this.subscriber.on(`channelStream:${this.channelId}`, this.onEvent);
 		this.emitTypersIntervalId = setInterval(this.emitTypers, 5000);
 	}
diff --git a/src/server/api/stream/types.ts b/src/server/api/stream/types.ts
index 18eba58538..7d2e918af6 100644
--- a/src/server/api/stream/types.ts
+++ b/src/server/api/stream/types.ts
@@ -1,7 +1,6 @@
 import { User } from '@/models/entities/user';
 import { EventEmitter } from 'events';
 import Emitter from 'strict-event-emitter-types';
-import StreamTypes from 'misskey-js/built/streaming.types';
 import { Channel } from '@/models/entities/channel';
 import { UserProfile } from '@/models/entities/user-profile';
 import { PackedUser } from '@/models/repositories/user';
@@ -14,6 +13,16 @@ import { PackedDriveFolder } from '@/models/repositories/drive-folder';
 import { DriveFolder } from '@/models/entities/drive-folder';
 import { Note } from '@/models/entities/note';
 import { Emoji } from '@/models/entities/emoji';
+import { UserList } from '@/models/entities/user-list';
+import { MessagingMessage } from '@/models/entities/messaging-message';
+import { PackedMessagingMessage } from '@/models/repositories/messaging-message';
+import { UserGroup } from '@/models/entities/user-group';
+import { PackedReversiGame } from '@/models/repositories/games/reversi/game';
+import { PackedReversiMatching } from '@/models/repositories/games/reversi/matching';
+import { ReversiGame } from '@/models/entities/games/reversi/game';
+import { AbuseUserReport } from '@/models/entities/abuse-user-report';
+import { PackedEmoji } from '@/models/repositories/emoji';
+import StreamTypes from 'misskey-js/built/streaming.types';
 
 // 辞書(interface or type)から{ type, body }ユニオンを定義
 // https://stackoverflow.com/questions/49311989/can-i-infer-the-type-of-a-value-using-extends-keyof-type
@@ -25,12 +34,6 @@ type EventUnionFromDictionary<
 // (payload: P) => void からPを取り出す
 type Payload<T extends (payload: any) => void> = T extends (payload: infer P) => void ? P : never;
 
-// misskey.jsのstreaming.typesの辞書から{ type, body }ユニオンを定義
-type EventUnionFromMkJSTypes<
-	T extends { [key: string]: ((payload: any) => void) | (() => void) },
-	U = { [K in keyof T]: { type: K; body: Payload<T[K]>} }
-> = U[keyof U];
-
 //#region Stream type-body definitions
 export interface InternalStreamTypes {
 	antennaCreated: Antenna;
@@ -38,6 +41,10 @@ export interface InternalStreamTypes {
 	antennaUpdated: Antenna;
 }
 
+export interface BroadcastTypes {
+	emojiAdded: PackedEmoji;
+};
+
 export interface UserStreamTypes {
 	terminate: {};
 	followChannel: Channel;
@@ -75,6 +82,7 @@ export interface MainStreamTypes {
 	readAllChannels: never;
 	unreadChannel: never;
 	myTokenRegenerated: never;
+	reversiInvited: PackedReversiMatching;
 }
 
 export interface DriveStreamTypes {
@@ -117,7 +125,92 @@ export interface NoteStreamTypes {
 	};
 }
 
+export interface ChannelStreamTypes {
+	typing: User['id'];
+}
+
+export interface UserListStreamTypes {
+	userAdded: PackedUser;
+	userRemoved: PackedUser;
+}
+
+export interface AntennaStreamTypes {
+	note: Note;
+}
+
+export interface MessagingStreamTypes {
+	read: MessagingMessage['id'][];
+	typing: User['id'];
+	message: PackedMessagingMessage;
+	deleted: MessagingMessage['id'];
+}
+
+export interface GroupMessagingStreamTypes {
+	read: {
+		ids: MessagingMessage['id'][];
+		userId: User['id'];
+	};
+	typing: User['id'];
+	message: PackedMessagingMessage;
+	deleted: MessagingMessage['id'];
+}
+
+export interface MessagingIndexStreamTypes {
+	read: MessagingMessage['id'][];
+	message: PackedMessagingMessage;
+}
+
+export interface ReversiStreamTypes {
+	matched: PackedReversiGame;
+	invited: PackedReversiMatching;
+}
+
+export interface ReversiGameStreamTypes {
+	started: PackedReversiGame;
+	ended: {
+		winnerId: User['id'],
+		game: PackedReversiGame;
+	};
+	updateSettings: {
+		key: string;
+		value: FIXME;
+	};
+	initForm: {
+		userId: User['id'];
+		form: FIXME;
+	};
+	updateForm: {
+		userId: User['id'];
+		id: string;
+		value: FIXME;
+	};
+	message: {
+		userId: User['id'];
+		message: FIXME;
+	};
+	changeAccepts: {
+		user1: boolean;
+		user2: boolean;
+	};
+	set: {
+		at: Date;
+		color: boolean;
+		pos: number;
+		next: boolean;
+	};
+	watching: User['id'];
+}
+
+export interface AdminStreamTypes {
+	newAbuseUserReport: {
+		id: AbuseUserReport['id'];
+		targetUserId: User['id'],
+		reporterId: User['id'],
+		comment: string;
+	}
+}
 //#endregion
+
 //#region 名前とメッセージのペアを中間生成
 interface StreamMessages {
 	internal: {
@@ -126,7 +219,7 @@ interface StreamMessages {
 	};
 	broadcast: {
 		name: 'broadcast';
-		spec: EventUnionFromMkJSTypes<StreamTypes.BroadcasrEvents>;
+		spec: EventUnionFromDictionary<BroadcastTypes>;
 	};
 	user: {
 		name: `user:${User['id']}`;
@@ -143,11 +236,52 @@ interface StreamMessages {
 	note: {
 		name: `noteStream:${Note['id']}`;
 		spec: EventUnionFromDictionary<NoteStreamTypes>;
+	};
+	channel: {
+		name: `channelStream:${Channel['id']}`;
+		spec: EventUnionFromDictionary<ChannelStreamTypes>;
+	};
+	userList: {
+		name: `userListStream:${UserList['id']}`;
+		spec: EventUnionFromDictionary<UserListStreamTypes>;
+	};
+	antenna: {
+		name: `antennaStream:${Antenna['id']}`;
+		spec: EventUnionFromDictionary<AntennaStreamTypes>;
+	};
+	messaging: {
+		name: `messagingStream:${User['id']}-${User['id']}`;
+		spec: EventUnionFromDictionary<MessagingStreamTypes>;
+	};
+	groupMessaging: {
+		name: `messagingStream:${UserGroup['id']}`;
+		spec: EventUnionFromDictionary<GroupMessagingStreamTypes>;
+	};
+	messagingIndex: {
+		name: `messagingIndexStream:${User['id']}`;
+		spec: EventUnionFromDictionary<MessagingIndexStreamTypes>;
+	};
+	reversi: {
+		name: `reversiStream:${User['id']}`;
+		spec: EventUnionFromDictionary<ReversiStreamTypes>;
+	};
+	reversiGame: {
+		name: `reversiGameStream:${ReversiGame['id']}`;
+		spec: EventUnionFromDictionary<ReversiGameStreamTypes>;
+	};
+	admin: {
+		name: `adminStream:${User['id']}`;
+		spec: EventUnionFromDictionary<AdminStreamTypes>;
 	}
+	// and notesStream (specにPackedNoteを突っ込むとなぜかバグる)
 }
-
 //#endregion
 
 // API event definitions
 type EventsGenerater<K extends keyof StreamMessages> = { [key in StreamMessages[K]['name']]: (e: StreamMessages[K]['spec']) => void };
-export type StreamEventEmitter = Emitter<EventEmitter, EventsGenerater<keyof StreamMessages>>;
+type NotesStreamEvent = { notesStream: (e: PackedNote) => void };
+export type StreamEventEmitter = Emitter<EventEmitter, EventsGenerater<keyof StreamMessages> & NotesStreamEvent>;
+
+// Channel Union
+type ChannelsUnionGenerater<K extends keyof StreamMessages> = StreamMessages[K]['name'];
+export type Channels = ChannelsUnionGenerater<keyof StreamMessages> | 'notesStream';
diff --git a/src/services/stream.ts b/src/services/stream.ts
index 4db1a77395..20c019f57d 100644
--- a/src/services/stream.ts
+++ b/src/services/stream.ts
@@ -7,9 +7,10 @@ import { UserGroup } from '@/models/entities/user-group';
 import config from '@/config/index';
 import { Antenna } from '@/models/entities/antenna';
 import { Channel } from '@/models/entities/channel';
+import { BroadcastTypes, Channels, InternalStreamTypes, UserStreamTypes } from '@/server/api/stream/types';
 
 class Publisher {
-	private publish = (channel: string, type: string | null, value?: any): void => {
+	private publish = (channel: Channels, type: string | null, value?: any): void => {
 		const message = type == null ? value : value == null ?
 			{ type: type, body: null } :
 			{ type: type, body: value };
@@ -20,15 +21,15 @@ class Publisher {
 		}));
 	}
 
-	public publishInternalEvent = (type: string, value?: any): void => {
+	public publishInternalEvent = <K extends keyof InternalStreamTypes>(type: K, value: InternalStreamTypes[K]): void => {
 		this.publish('internal', type, typeof value === 'undefined' ? null : value);
 	}
 
-	public publishUserEvent = (userId: User['id'], type: string, value?: any): void => {
+	public publishUserEvent = <K extends keyof UserStreamTypes>(userId: User['id'], type: K, value: UserStreamTypes[K]): void => {
 		this.publish(`user:${userId}`, type, typeof value === 'undefined' ? null : value);
 	}
 
-	public publishBroadcastStream = (type: string, value?: any): void => {
+	public publishBroadcastStream = <K extends keyof BroadcastTypes>(type: K, value: BroadcastTypes[K]): void => {
 		this.publish('broadcast', type, typeof value === 'undefined' ? null : value);
 	}
 

From 5db28922d04c9496dc6482421eeaeeeba6f97d57 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 Sep 2021 22:47:27 +0900
Subject: [PATCH 26/91] wip

---
 src/models/repositories/emoji.ts              |  3 +-
 src/models/repositories/games/reversi/game.ts |  3 +-
 .../repositories/games/reversi/matching.ts    |  3 +-
 src/models/repositories/signin.ts             |  5 +-
 src/prelude/types.ts                          |  1 +
 src/server/api/stream/types.ts                | 59 +++++++++++++------
 src/services/stream.ts                        |  6 +-
 7 files changed, 54 insertions(+), 26 deletions(-)
 create mode 100644 src/prelude/types.ts

diff --git a/src/models/repositories/emoji.ts b/src/models/repositories/emoji.ts
index 1bbe6b8215..2dc5f5282a 100644
--- a/src/models/repositories/emoji.ts
+++ b/src/models/repositories/emoji.ts
@@ -1,7 +1,8 @@
 import { EntityRepository, Repository } from 'typeorm';
 import { Emoji } from '@/models/entities/emoji';
+import { Resolved } from '@/prelude/types';
 
-export type PackedEmoji = FIXME;
+export type PackedEmoji = Resolved<ReturnType<EmojiRepository['pack']>>;
 
 @EntityRepository(Emoji)
 export class EmojiRepository extends Repository<Emoji> {
diff --git a/src/models/repositories/games/reversi/game.ts b/src/models/repositories/games/reversi/game.ts
index eb2561be91..10433bf58f 100644
--- a/src/models/repositories/games/reversi/game.ts
+++ b/src/models/repositories/games/reversi/game.ts
@@ -2,8 +2,9 @@ import { User } from '@/models/entities/user';
 import { EntityRepository, Repository } from 'typeorm';
 import { Users } from '../../../index';
 import { ReversiGame } from '@/models/entities/games/reversi/game';
+import { Resolved } from '@/prelude/types';
 
-export type PackedReversiGame = FIXME;
+export type PackedReversiGame = Resolved<ReturnType<ReversiGameRepository['pack']>>;
 
 @EntityRepository(ReversiGame)
 export class ReversiGameRepository extends Repository<ReversiGame> {
diff --git a/src/models/repositories/games/reversi/matching.ts b/src/models/repositories/games/reversi/matching.ts
index 91615091ac..2696f1f8ea 100644
--- a/src/models/repositories/games/reversi/matching.ts
+++ b/src/models/repositories/games/reversi/matching.ts
@@ -3,8 +3,9 @@ import { ReversiMatching } from '@/models/entities/games/reversi/matching';
 import { Users } from '../../../index';
 import { awaitAll } from '@/prelude/await-all';
 import { User } from '@/models/entities/user';
+import { Resolved } from '@/prelude/types';
 
-export type PackedReversiMatching = FIXME;
+export type PackedReversiMatching = Resolved<ReturnType<ReversiMatchingRepository['pack']>>;
 
 @EntityRepository(ReversiMatching)
 export class ReversiMatchingRepository extends Repository<ReversiMatching> {
diff --git a/src/models/repositories/signin.ts b/src/models/repositories/signin.ts
index 9942d2d962..79d23241a7 100644
--- a/src/models/repositories/signin.ts
+++ b/src/models/repositories/signin.ts
@@ -1,10 +1,13 @@
 import { EntityRepository, Repository } from 'typeorm';
 import { Signin } from '@/models/entities/signin';
+import { Resolved } from '@/prelude/types';
+
+export type PackedSignin = Resolved<ReturnType<SigninRepository['pack']>>;
 
 @EntityRepository(Signin)
 export class SigninRepository extends Repository<Signin> {
 	public async pack(
-		src: any,
+		src: Signin,
 	) {
 		return src;
 	}
diff --git a/src/prelude/types.ts b/src/prelude/types.ts
new file mode 100644
index 0000000000..d004d0ff31
--- /dev/null
+++ b/src/prelude/types.ts
@@ -0,0 +1 @@
+export type Resolved<P extends Promise<any>> = P extends Promise<infer R> ? R : never;
diff --git a/src/server/api/stream/types.ts b/src/server/api/stream/types.ts
index 7d2e918af6..db947bd1f4 100644
--- a/src/server/api/stream/types.ts
+++ b/src/server/api/stream/types.ts
@@ -23,6 +23,8 @@ import { ReversiGame } from '@/models/entities/games/reversi/game';
 import { AbuseUserReport } from '@/models/entities/abuse-user-report';
 import { PackedEmoji } from '@/models/repositories/emoji';
 import StreamTypes from 'misskey-js/built/streaming.types';
+import { PackedSignin } from '@/models/repositories/signin';
+import { Page } from '@/models/entities/page';
 
 // 辞書(interface or type)から{ type, body }ユニオンを定義
 // https://stackoverflow.com/questions/49311989/can-i-infer-the-type-of-a-value-using-extends-keyof-type
@@ -43,7 +45,7 @@ export interface InternalStreamTypes {
 
 export interface BroadcastTypes {
 	emojiAdded: PackedEmoji;
-};
+}
 
 export interface UserStreamTypes {
 	terminate: {};
@@ -66,23 +68,42 @@ export interface MainStreamTypes {
 	followed: PackedUser;
 	unfollow: PackedUser;
 	meUpdated: PackedUser;
-	pageEvent: Payload<StreamTypes.Channels['main']['events']['pageEvent']>;
-	urlUploadFinished: Payload<StreamTypes.Channels['main']['events']['urlUploadFinished']>;
-	readAllNotifications: never;
-	unreadNotification: never;
-	unreadMention: never;
-	readAllUnreadMentions: never;
-	unreadSpecifiedNote: never;
-	readAllUnreadSpecifiedNotes: never;
-	readAllMessagingMessages: never;
-	unreadMessagingMessage: never;
-	readAllAntennas: never;
-	unreadAntenna: never;
-	readAllAnnouncements: never;
-	readAllChannels: never;
-	unreadChannel: never;
-	myTokenRegenerated: never;
+	pageEvent: {
+		pageId: Page['id'];
+		event: string;
+		var: any;
+		userId: User['id'];
+		user: PackedUser;
+	};
+	urlUploadFinished: {
+		marker?: string | null;
+		file: PackedDriveFile;
+	};
+	readAllNotifications: undefined;
+	unreadNotification: PackedNotification;
+	unreadMention: Note['id'];
+	readAllUnreadMentions: undefined;
+	unreadSpecifiedNote: Note['id'];
+	readAllUnreadSpecifiedNotes: undefined;
+	readAllMessagingMessages: undefined;
+	messagingMessage: PackedMessagingMessage;
+	unreadMessagingMessage: PackedMessagingMessage;
+	readAllAntennas: undefined;
+	unreadAntenna: Antenna;
+	readAllAnnouncements: undefined;
+	readAllChannels: undefined;
+	unreadChannel: Note['id'];
+	myTokenRegenerated: undefined;
+	reversiNoInvites: undefined;
 	reversiInvited: PackedReversiMatching;
+	signin: PackedSignin;
+	registryUpdated: {
+		scope?: string[];
+		key: string;
+		value: any | null;
+	};
+	driveFileCreated: PackedDriveFile;
+	readAntenna: Antenna;
 }
 
 export interface DriveStreamTypes {
@@ -207,7 +228,7 @@ export interface AdminStreamTypes {
 		targetUserId: User['id'],
 		reporterId: User['id'],
 		comment: string;
-	}
+	};
 }
 //#endregion
 
@@ -272,7 +293,7 @@ interface StreamMessages {
 	admin: {
 		name: `adminStream:${User['id']}`;
 		spec: EventUnionFromDictionary<AdminStreamTypes>;
-	}
+	};
 	// and notesStream (specにPackedNoteを突っ込むとなぜかバグる)
 }
 //#endregion
diff --git a/src/services/stream.ts b/src/services/stream.ts
index 20c019f57d..3b0c90d75c 100644
--- a/src/services/stream.ts
+++ b/src/services/stream.ts
@@ -7,7 +7,7 @@ import { UserGroup } from '@/models/entities/user-group';
 import config from '@/config/index';
 import { Antenna } from '@/models/entities/antenna';
 import { Channel } from '@/models/entities/channel';
-import { BroadcastTypes, Channels, InternalStreamTypes, UserStreamTypes } from '@/server/api/stream/types';
+import { BroadcastTypes, Channels, InternalStreamTypes, MainStreamTypes, UserStreamTypes } from '@/server/api/stream/types';
 
 class Publisher {
 	private publish = (channel: Channels, type: string | null, value?: any): void => {
@@ -25,7 +25,7 @@ class Publisher {
 		this.publish('internal', type, typeof value === 'undefined' ? null : value);
 	}
 
-	public publishUserEvent = <K extends keyof UserStreamTypes>(userId: User['id'], type: K, value: UserStreamTypes[K]): void => {
+	public publishUserEvent = <K extends keyof UserStreamTypes>(userId: User['id'], type: K, value?: UserStreamTypes[K]): void => {
 		this.publish(`user:${userId}`, type, typeof value === 'undefined' ? null : value);
 	}
 
@@ -33,7 +33,7 @@ class Publisher {
 		this.publish('broadcast', type, typeof value === 'undefined' ? null : value);
 	}
 
-	public publishMainStream = (userId: User['id'], type: string, value?: any): void => {
+	public publishMainStream = <K extends keyof MainStreamTypes>(userId: User['id'], type: K, value?: MainStreamTypes[K]): void => {
 		this.publish(`mainStream:${userId}`, type, typeof value === 'undefined' ? null : value);
 	}
 

From f6853b22547426a58f68d8e89361daef60f81d71 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 Sep 2021 22:48:12 +0900
Subject: [PATCH 27/91] :v:

---
 src/server/api/stream/types.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/server/api/stream/types.ts b/src/server/api/stream/types.ts
index db947bd1f4..17acc95379 100644
--- a/src/server/api/stream/types.ts
+++ b/src/server/api/stream/types.ts
@@ -22,7 +22,6 @@ import { PackedReversiMatching } from '@/models/repositories/games/reversi/match
 import { ReversiGame } from '@/models/entities/games/reversi/game';
 import { AbuseUserReport } from '@/models/entities/abuse-user-report';
 import { PackedEmoji } from '@/models/repositories/emoji';
-import StreamTypes from 'misskey-js/built/streaming.types';
 import { PackedSignin } from '@/models/repositories/signin';
 import { Page } from '@/models/entities/page';
 

From 4793d1cc56e5d0778f54a1a4b45fc3cdc09ea172 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Mon, 6 Sep 2021 23:18:16 +0900
Subject: [PATCH 28/91] =?UTF-8?q?=E9=80=81=E4=BF=A1=E5=81=B4=E3=81=AB?=
 =?UTF-8?q?=E5=9E=8B=E3=82=A8=E3=83=A9=E3=83=BC=E3=81=8C=E3=81=AA=E3=81=84?=
 =?UTF-8?q?=E3=81=93=E3=81=A8=E3=82=923=E5=9B=9E=E7=A2=BA=E8=AA=8D?=
 =?UTF-8?q?=E3=81=97=E3=81=9F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/server/api/stream/types.ts | 59 +++++++++++++++-------------------
 src/services/stream.ts         | 46 ++++++++++++++++++--------
 2 files changed, 58 insertions(+), 47 deletions(-)

diff --git a/src/server/api/stream/types.ts b/src/server/api/stream/types.ts
index 17acc95379..b78b894977 100644
--- a/src/server/api/stream/types.ts
+++ b/src/server/api/stream/types.ts
@@ -1,27 +1,27 @@
-import { User } from '@/models/entities/user';
 import { EventEmitter } from 'events';
 import Emitter from 'strict-event-emitter-types';
 import { Channel } from '@/models/entities/channel';
+import { User } from '@/models/entities/user';
 import { UserProfile } from '@/models/entities/user-profile';
 import { PackedUser } from '@/models/repositories/user';
 import { PackedNotification } from '@/models/repositories/notification';
+import { Note } from '@/models/entities/note';
 import { PackedNote } from '@/models/repositories/note';
 import { Antenna } from '@/models/entities/antenna';
 import { DriveFile } from '@/models/entities/drive-file';
 import { PackedDriveFile } from '@/models/repositories/drive-file';
-import { PackedDriveFolder } from '@/models/repositories/drive-folder';
 import { DriveFolder } from '@/models/entities/drive-folder';
-import { Note } from '@/models/entities/note';
+import { PackedDriveFolder } from '@/models/repositories/drive-folder';
 import { Emoji } from '@/models/entities/emoji';
+import { PackedEmoji } from '@/models/repositories/emoji';
 import { UserList } from '@/models/entities/user-list';
 import { MessagingMessage } from '@/models/entities/messaging-message';
 import { PackedMessagingMessage } from '@/models/repositories/messaging-message';
 import { UserGroup } from '@/models/entities/user-group';
+import { ReversiGame } from '@/models/entities/games/reversi/game';
 import { PackedReversiGame } from '@/models/repositories/games/reversi/game';
 import { PackedReversiMatching } from '@/models/repositories/games/reversi/matching';
-import { ReversiGame } from '@/models/entities/games/reversi/game';
 import { AbuseUserReport } from '@/models/entities/abuse-user-report';
-import { PackedEmoji } from '@/models/repositories/emoji';
 import { PackedSignin } from '@/models/repositories/signin';
 import { Page } from '@/models/entities/page';
 
@@ -32,9 +32,6 @@ type EventUnionFromDictionary<
 	U = { [K in keyof T]: { type: K; body: T[K]} }
 > = U[keyof U];
 
-// (payload: P) => void からPを取り出す
-type Payload<T extends (payload: any) => void> = T extends (payload: infer P) => void ? P : never;
-
 //#region Stream type-body definitions
 export interface InternalStreamTypes {
 	antennaCreated: Antenna;
@@ -43,7 +40,9 @@ export interface InternalStreamTypes {
 }
 
 export interface BroadcastTypes {
-	emojiAdded: PackedEmoji;
+	emojiAdded: {
+		emoji: PackedEmoji;
+	};
 }
 
 export interface UserStreamTypes {
@@ -116,34 +115,28 @@ export interface DriveStreamTypes {
 
 export interface NoteStreamTypes {
 	pollVoted: {
-		id: Note['id'];
-		body: {
-			choice: number;
-			userId: User['id'];
-		};
+		choice: number;
+		userId: User['id'];
 	};
 	deleted: {
-		id: Note['id'];
-		body: {
-			deletedAt: Date;
-		};
+		deletedAt: Date;
 	};
 	reacted: {
-		id: Note['id'];
-		body: {
-			reaction: string;
-			emoji?: Emoji;
-			userId: User['id'];
-		};
+		reaction: string;
+		emoji?: Emoji;
+		userId: User['id'];
 	};
 	unreacted: {
-		id: Note['id'];
-		body: {
-			reaction: string;
-			userId: User['id'];
-		}
+		reaction: string;
+		userId: User['id'];
 	};
 }
+type NoteStreamEventTypes = {
+	[key in keyof NoteStreamTypes]: {
+		id: Note['id'];
+		body: NoteStreamTypes[key];
+	};
+};
 
 export interface ChannelStreamTypes {
 	typing: User['id'];
@@ -188,7 +181,7 @@ export interface ReversiStreamTypes {
 export interface ReversiGameStreamTypes {
 	started: PackedReversiGame;
 	ended: {
-		winnerId: User['id'],
+		winnerId?: User['id'] | null,
 		game: PackedReversiGame;
 	};
 	updateSettings: {
@@ -255,7 +248,7 @@ interface StreamMessages {
 	};
 	note: {
 		name: `noteStream:${Note['id']}`;
-		spec: EventUnionFromDictionary<NoteStreamTypes>;
+		spec: EventUnionFromDictionary<NoteStreamEventTypes>;
 	};
 	channel: {
 		name: `channelStream:${Channel['id']}`;
@@ -302,6 +295,6 @@ type EventsGenerater<K extends keyof StreamMessages> = { [key in StreamMessages[
 type NotesStreamEvent = { notesStream: (e: PackedNote) => void };
 export type StreamEventEmitter = Emitter<EventEmitter, EventsGenerater<keyof StreamMessages> & NotesStreamEvent>;
 
-// Channel Union
+// provide stream channels union
 type ChannelsUnionGenerater<K extends keyof StreamMessages> = StreamMessages[K]['name'];
-export type Channels = ChannelsUnionGenerater<keyof StreamMessages> | 'notesStream';
+export type StreamChannels = ChannelsUnionGenerater<keyof StreamMessages> | 'notesStream';
diff --git a/src/services/stream.ts b/src/services/stream.ts
index 3b0c90d75c..3c5ef36399 100644
--- a/src/services/stream.ts
+++ b/src/services/stream.ts
@@ -7,10 +7,28 @@ import { UserGroup } from '@/models/entities/user-group';
 import config from '@/config/index';
 import { Antenna } from '@/models/entities/antenna';
 import { Channel } from '@/models/entities/channel';
-import { BroadcastTypes, Channels, InternalStreamTypes, MainStreamTypes, UserStreamTypes } from '@/server/api/stream/types';
+import {
+	StreamChannels,
+	AdminStreamTypes,
+	AntennaStreamTypes,
+	BroadcastTypes,
+	ChannelStreamTypes,
+	DriveStreamTypes,
+	GroupMessagingStreamTypes,
+	InternalStreamTypes,
+	MainStreamTypes,
+	MessagingIndexStreamTypes,
+	MessagingStreamTypes,
+	NoteStreamTypes,
+	ReversiGameStreamTypes,
+	ReversiStreamTypes,
+	UserListStreamTypes,
+	UserStreamTypes
+} from '@/server/api/stream/types';
+import { PackedNote } from '@/models/repositories/note';
 
 class Publisher {
-	private publish = (channel: Channels, type: string | null, value?: any): void => {
+	private publish = (channel: StreamChannels, type: string | null, value?: any): void => {
 		const message = type == null ? value : value == null ?
 			{ type: type, body: null } :
 			{ type: type, body: value };
@@ -37,54 +55,54 @@ class Publisher {
 		this.publish(`mainStream:${userId}`, type, typeof value === 'undefined' ? null : value);
 	}
 
-	public publishDriveStream = (userId: User['id'], type: string, value?: any): void => {
+	public publishDriveStream = <K extends keyof DriveStreamTypes>(userId: User['id'], type: K, value?: DriveStreamTypes[K]): void => {
 		this.publish(`driveStream:${userId}`, type, typeof value === 'undefined' ? null : value);
 	}
 
-	public publishNoteStream = (noteId: Note['id'], type: string, value: any): void => {
+	public publishNoteStream = <K extends keyof NoteStreamTypes>(noteId: Note['id'], type: K, value: NoteStreamTypes[K]): void => {
 		this.publish(`noteStream:${noteId}`, type, {
 			id: noteId,
 			body: value
 		});
 	}
 
-	public publishChannelStream = (channelId: Channel['id'], type: string, value?: any): void => {
+	public publishChannelStream = <K extends keyof ChannelStreamTypes>(channelId: Channel['id'], type: K, value?: ChannelStreamTypes[K]): void => {
 		this.publish(`channelStream:${channelId}`, type, typeof value === 'undefined' ? null : value);
 	}
 
-	public publishUserListStream = (listId: UserList['id'], type: string, value?: any): void => {
+	public publishUserListStream = <K extends keyof UserListStreamTypes>(listId: UserList['id'], type: K, value?: UserListStreamTypes[K]): void => {
 		this.publish(`userListStream:${listId}`, type, typeof value === 'undefined' ? null : value);
 	}
 
-	public publishAntennaStream = (antennaId: Antenna['id'], type: string, value?: any): void => {
+	public publishAntennaStream = <K extends keyof AntennaStreamTypes>(antennaId: Antenna['id'], type: K, value?: AntennaStreamTypes[K]): void => {
 		this.publish(`antennaStream:${antennaId}`, type, typeof value === 'undefined' ? null : value);
 	}
 
-	public publishMessagingStream = (userId: User['id'], otherpartyId: User['id'], type: string, value?: any): void => {
+	public publishMessagingStream = <K extends keyof MessagingStreamTypes>(userId: User['id'], otherpartyId: User['id'], type: K, value?: MessagingStreamTypes[K]): void => {
 		this.publish(`messagingStream:${userId}-${otherpartyId}`, type, typeof value === 'undefined' ? null : value);
 	}
 
-	public publishGroupMessagingStream = (groupId: UserGroup['id'], type: string, value?: any): void => {
+	public publishGroupMessagingStream = <K extends keyof GroupMessagingStreamTypes>(groupId: UserGroup['id'], type: K, value?: GroupMessagingStreamTypes[K]): void => {
 		this.publish(`messagingStream:${groupId}`, type, typeof value === 'undefined' ? null : value);
 	}
 
-	public publishMessagingIndexStream = (userId: User['id'], type: string, value?: any): void => {
+	public publishMessagingIndexStream = <K extends keyof MessagingIndexStreamTypes>(userId: User['id'], type: K, value?: MessagingIndexStreamTypes[K]): void => {
 		this.publish(`messagingIndexStream:${userId}`, type, typeof value === 'undefined' ? null : value);
 	}
 
-	public publishReversiStream = (userId: User['id'], type: string, value?: any): void => {
+	public publishReversiStream = <K extends keyof ReversiStreamTypes>(userId: User['id'], type: K, value?: ReversiStreamTypes[K]): void => {
 		this.publish(`reversiStream:${userId}`, type, typeof value === 'undefined' ? null : value);
 	}
 
-	public publishReversiGameStream = (gameId: ReversiGame['id'], type: string, value?: any): void => {
+	public publishReversiGameStream = <K extends keyof ReversiGameStreamTypes>(gameId: ReversiGame['id'], type: K, value?: ReversiGameStreamTypes[K]): void => {
 		this.publish(`reversiGameStream:${gameId}`, type, typeof value === 'undefined' ? null : value);
 	}
 
-	public publishNotesStream = (note: any): void => {
+	public publishNotesStream = (note: PackedNote): void => {
 		this.publish('notesStream', null, note);
 	}
 
-	public publishAdminStream = (userId: User['id'], type: string, value?: any): void => {
+	public publishAdminStream = <K extends keyof AdminStreamTypes>(userId: User['id'], type: K, value?: AdminStreamTypes[K]): void => {
 		this.publish(`adminStream:${userId}`, type, typeof value === 'undefined' ? null : value);
 	}
 }

From be47a120d75996864b6bc8a2f393a3ed87aebbb2 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Tue, 7 Sep 2021 02:40:38 +0900
Subject: [PATCH 29/91] :v:

---
 src/server/api/stream/channels/messaging.ts |  6 ++++--
 src/server/api/stream/index.ts              |  8 ++++----
 src/server/api/stream/types.ts              | 20 +++++++++++---------
 3 files changed, 19 insertions(+), 15 deletions(-)

diff --git a/src/server/api/stream/channels/messaging.ts b/src/server/api/stream/channels/messaging.ts
index 015b0a7650..a75181d6d7 100644
--- a/src/server/api/stream/channels/messaging.ts
+++ b/src/server/api/stream/channels/messaging.ts
@@ -3,6 +3,8 @@ import { readUserMessagingMessage, readGroupMessagingMessage, deliverReadActivit
 import Channel from '../channel';
 import { UserGroupJoinings, Users, MessagingMessages } from '@/models/index';
 import { User, ILocalUser, IRemoteUser } from '@/models/entities/user';
+import { UserGroup } from '@/models/entities/user-group';
+import { StreamMessages } from '../types';
 
 export default class extends Channel {
 	public readonly chName = 'messaging';
@@ -12,7 +14,7 @@ export default class extends Channel {
 	private otherpartyId: string | null;
 	private otherparty: User | null;
 	private groupId: string | null;
-	private subCh: string;
+	private subCh: `messagingStream:${User['id']}-${User['id']}` | `messagingStream:${UserGroup['id']}`;
 	private typers: Record<User['id'], Date> = {};
 	private emitTypersIntervalId: ReturnType<typeof setInterval>;
 
@@ -45,7 +47,7 @@ export default class extends Channel {
 	}
 
 	@autobind
-	private onEvent(data: any) {
+	private onEvent(data: StreamMessages['messaging']['spec'] | StreamMessages['groupMessaging']['spec']) {
 		if (data.type === 'typing') {
 			const id = data.body;
 			const begin = this.typers[id] == null;
diff --git a/src/server/api/stream/index.ts b/src/server/api/stream/index.ts
index b2cecb4470..89d9889220 100644
--- a/src/server/api/stream/index.ts
+++ b/src/server/api/stream/index.ts
@@ -15,7 +15,7 @@ import { UserProfile } from '@/models/entities/user-profile';
 import { publishChannelStream, publishGroupMessagingStream, publishMessagingStream } from '@/services/stream';
 import { UserGroup } from '@/models/entities/user-group';
 import { PackedNote } from '@/models/repositories/note';
-import { StreamEventEmitter, UserStreams } from './types';
+import { StreamEventEmitter, StreamMessages } from './types';
 
 /**
  * Main stream connection
@@ -47,8 +47,8 @@ export default class Connection {
 
 		this.wsConnection.on('message', this.onWsConnectionMessage);
 
-		this.subscriber.on('broadcast', async ({ type, body }) => {
-			this.onBroadcastMessage(type, body);
+		this.subscriber.on('broadcast', async ev => {
+			this.onBroadcastMessage(ev.type, ev.body);
 		});
 
 		if (this.user) {
@@ -65,7 +65,7 @@ export default class Connection {
 	}
 
 	@autobind
-	private onUserEvent(ev: UserStreams) { // { type, body }と展開すると型も展開されてしまう
+	private onUserEvent(ev: StreamMessages['user']['spec']) { // { type, body }と展開すると型も展開されてしまう
 		switch (ev.type) {
 			case 'follow':
 				this.following.add(ev.body.id);
diff --git a/src/server/api/stream/types.ts b/src/server/api/stream/types.ts
index b78b894977..eb80c2e913 100644
--- a/src/server/api/stream/types.ts
+++ b/src/server/api/stream/types.ts
@@ -29,7 +29,7 @@ import { Page } from '@/models/entities/page';
 // https://stackoverflow.com/questions/49311989/can-i-infer-the-type-of-a-value-using-extends-keyof-type
 type EventUnionFromDictionary<
 	T extends object,
-	U = { [K in keyof T]: { type: K; body: T[K]} }
+	U = { [K in keyof T]: { type: K; body: T[K]; } }
 > = U[keyof U];
 
 //#region Stream type-body definitions
@@ -225,7 +225,7 @@ export interface AdminStreamTypes {
 //#endregion
 
 //#region 名前とメッセージのペアを中間生成
-interface StreamMessages {
+export type StreamMessages = {
 	internal: {
 		name: 'internal';
 		spec: EventUnionFromDictionary<InternalStreamTypes>;
@@ -286,15 +286,17 @@ interface StreamMessages {
 		name: `adminStream:${User['id']}`;
 		spec: EventUnionFromDictionary<AdminStreamTypes>;
 	};
-	// and notesStream (specにPackedNoteを突っ込むとなぜかバグる)
-}
+	notes: {
+		name: 'notesStream';
+		spec: PackedNote;
+	};
+};
 //#endregion
 
 // API event definitions
-type EventsGenerater<K extends keyof StreamMessages> = { [key in StreamMessages[K]['name']]: (e: StreamMessages[K]['spec']) => void };
-type NotesStreamEvent = { notesStream: (e: PackedNote) => void };
-export type StreamEventEmitter = Emitter<EventEmitter, EventsGenerater<keyof StreamMessages> & NotesStreamEvent>;
+type EventsDictionary = { [x in keyof StreamMessages]: { [y in StreamMessages[x]['name']]: (e: StreamMessages[x]['spec']) => void } };
+type Events<D> = (D extends any ? (k: D) => void : never) extends ((k: infer E) => void) ? E : never;
+export type StreamEventEmitter = Emitter<EventEmitter, Events<EventsDictionary[keyof StreamMessages]>>;
 
 // provide stream channels union
-type ChannelsUnionGenerater<K extends keyof StreamMessages> = StreamMessages[K]['name'];
-export type StreamChannels = ChannelsUnionGenerater<keyof StreamMessages> | 'notesStream';
+export type StreamChannels = StreamMessages[keyof StreamMessages]['name'] | 'notesStream';

From fd55e2b5b5c9599eec19ed58a6c6e4ca9ed000c7 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Tue, 7 Sep 2021 03:31:56 +0900
Subject: [PATCH 30/91] wip

---
 src/server/api/stream/types.ts | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/src/server/api/stream/types.ts b/src/server/api/stream/types.ts
index eb80c2e913..b497458ce5 100644
--- a/src/server/api/stream/types.ts
+++ b/src/server/api/stream/types.ts
@@ -294,9 +294,13 @@ export type StreamMessages = {
 //#endregion
 
 // API event definitions
-type EventsDictionary = { [x in keyof StreamMessages]: { [y in StreamMessages[x]['name']]: (e: StreamMessages[x]['spec']) => void } };
-type Events<D> = (D extends any ? (k: D) => void : never) extends ((k: infer E) => void) ? E : never;
-export type StreamEventEmitter = Emitter<EventEmitter, Events<EventsDictionary[keyof StreamMessages]>>;
+// ストリームごとのEmitterの辞書を用意
+type EventsDictionary = { [x in keyof StreamMessages]: Emitter<EventEmitter, { [y in StreamMessages[x]['name']]: (e: StreamMessages[x]['spec']) => void }> };
+// 共用体型を交差型にする型 https://stackoverflow.com/questions/54938141/typescript-convert-union-to-intersection
+type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
+// Emitter辞書をストリームごとに共用体型にし、UnionToIntersectionで交差型にする
+export type StreamEventEmitter = UnionToIntersection<EventsDictionary[keyof StreamMessages]>;
+// そうしないとなぜかユニオン型が増えまくり、ts(2590)にひっかかる
 
 // provide stream channels union
 export type StreamChannels = StreamMessages[keyof StreamMessages]['name'] | 'notesStream';

From b22972f7009ca97bb62a79e054960e5464f814c8 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Tue, 7 Sep 2021 03:35:36 +0900
Subject: [PATCH 31/91] update typescript

---
 package.json | 2 +-
 yarn.lock    | 8 ++++----
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/package.json b/package.json
index ab24a1c7ec..4d25898e4e 100644
--- a/package.json
+++ b/package.json
@@ -229,7 +229,7 @@
 		"tslint-sonarts": "1.9.0",
 		"twemoji-parser": "13.1.0",
 		"typeorm": "0.2.37",
-		"typescript": "4.3.5",
+		"typescript": "4.4.2",
 		"ulid": "2.3.0",
 		"uuid": "8.3.2",
 		"v-debounce": "0.1.2",
diff --git a/yarn.lock b/yarn.lock
index 33f9f22454..fde80ff8d6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -11174,10 +11174,10 @@ typeorm@0.2.37:
     yargs "^17.0.1"
     zen-observable-ts "^1.0.0"
 
-typescript@4.3.5:
-  version "4.3.5"
-  resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4"
-  integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==
+typescript@4.4.2:
+  version "4.4.2"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.2.tgz#6d618640d430e3569a1dfb44f7d7e600ced3ee86"
+  integrity sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==
 
 uc.micro@^1.0.1, uc.micro@^1.0.5:
   version "1.0.6"

From 714f9f20325a71175cdc4c4cc2f7240b817152e8 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Tue, 7 Sep 2021 03:47:50 +0900
Subject: [PATCH 32/91] define items in full Schema

---
 src/misc/schema.ts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/misc/schema.ts b/src/misc/schema.ts
index 90d4c3b715..d27c9eff99 100644
--- a/src/misc/schema.ts
+++ b/src/misc/schema.ts
@@ -48,6 +48,7 @@ export const refs = {
 };
 
 export interface Schema extends SimpleSchema {
+	items?: Schema;
 	properties?: Obj;
 	ref?: keyof typeof refs;
 }

From 1590cf000fd282d0f0c600acab740e2f64bb5f06 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Tue, 7 Sep 2021 03:58:58 +0900
Subject: [PATCH 33/91] edit comment

---
 src/server/api/stream/types.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/server/api/stream/types.ts b/src/server/api/stream/types.ts
index b497458ce5..2670d1ac87 100644
--- a/src/server/api/stream/types.ts
+++ b/src/server/api/stream/types.ts
@@ -300,7 +300,7 @@ type EventsDictionary = { [x in keyof StreamMessages]: Emitter<EventEmitter, { [
 type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
 // Emitter辞書をストリームごとに共用体型にし、UnionToIntersectionで交差型にする
 export type StreamEventEmitter = UnionToIntersection<EventsDictionary[keyof StreamMessages]>;
-// そうしないとなぜかユニオン型が増えまくり、ts(2590)にひっかかる
+// そうしないとユニオンが増大してts(2590)にひっかかる
 
 // provide stream channels union
 export type StreamChannels = StreamMessages[keyof StreamMessages]['name'] | 'notesStream';

From cd36102b9002a0d6b15c3678871f95eb269f241a Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Tue, 7 Sep 2021 04:01:06 +0900
Subject: [PATCH 34/91] edit comment

---
 src/server/api/stream/types.ts | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/server/api/stream/types.ts b/src/server/api/stream/types.ts
index 2670d1ac87..2d44154559 100644
--- a/src/server/api/stream/types.ts
+++ b/src/server/api/stream/types.ts
@@ -224,7 +224,7 @@ export interface AdminStreamTypes {
 }
 //#endregion
 
-//#region 名前とメッセージのペアを中間生成
+// name/messages(spec) pairs dictionary
 export type StreamMessages = {
 	internal: {
 		name: 'internal';
@@ -291,7 +291,6 @@ export type StreamMessages = {
 		spec: PackedNote;
 	};
 };
-//#endregion
 
 // API event definitions
 // ストリームごとのEmitterの辞書を用意

From 2c494f10522024e25b748649cdc7de096459b131 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Tue, 7 Sep 2021 04:41:07 +0900
Subject: [PATCH 35/91] edit comment

---
 src/server/api/stream/types.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/server/api/stream/types.ts b/src/server/api/stream/types.ts
index 2d44154559..bf67ce90b2 100644
--- a/src/server/api/stream/types.ts
+++ b/src/server/api/stream/types.ts
@@ -299,7 +299,7 @@ type EventsDictionary = { [x in keyof StreamMessages]: Emitter<EventEmitter, { [
 type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
 // Emitter辞書をストリームごとに共用体型にし、UnionToIntersectionで交差型にする
 export type StreamEventEmitter = UnionToIntersection<EventsDictionary[keyof StreamMessages]>;
-// そうしないとユニオンが増大してts(2590)にひっかかる
+// { [y in name]: (e: spec) => void }をまとめてその交差型をEmitterにかけるとts(2590)にひっかかる
 
 // provide stream channels union
 export type StreamChannels = StreamMessages[keyof StreamMessages]['name'] | 'notesStream';

From afb7d5d56b4c88e03164c0e17c61ac592e44fa48 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Tue, 7 Sep 2021 09:32:33 +0900
Subject: [PATCH 36/91] Update src/prelude/types.ts
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
---
 src/prelude/types.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/prelude/types.ts b/src/prelude/types.ts
index d004d0ff31..b8c49f9292 100644
--- a/src/prelude/types.ts
+++ b/src/prelude/types.ts
@@ -1 +1 @@
-export type Resolved<P extends Promise<any>> = P extends Promise<infer R> ? R : never;
+export type Resolved<P> = P extends PromiseLike<infer R> ? Resolved<R> : never;

From 6a808ac5e3b770e620ab6b739398559904e4f125 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Tue, 7 Sep 2021 09:33:45 +0900
Subject: [PATCH 37/91] 
 https://github.com/misskey-dev/misskey/pull/7769#discussion_r703058458

---
 src/services/stream.ts | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/services/stream.ts b/src/services/stream.ts
index 3c5ef36399..c21ccb4a74 100644
--- a/src/services/stream.ts
+++ b/src/services/stream.ts
@@ -39,7 +39,7 @@ class Publisher {
 		}));
 	}
 
-	public publishInternalEvent = <K extends keyof InternalStreamTypes>(type: K, value: InternalStreamTypes[K]): void => {
+	public publishInternalEvent = <K extends keyof InternalStreamTypes>(type: K, value?: InternalStreamTypes[K]): void => {
 		this.publish('internal', type, typeof value === 'undefined' ? null : value);
 	}
 
@@ -47,7 +47,7 @@ class Publisher {
 		this.publish(`user:${userId}`, type, typeof value === 'undefined' ? null : value);
 	}
 
-	public publishBroadcastStream = <K extends keyof BroadcastTypes>(type: K, value: BroadcastTypes[K]): void => {
+	public publishBroadcastStream = <K extends keyof BroadcastTypes>(type: K, value?: BroadcastTypes[K]): void => {
 		this.publish('broadcast', type, typeof value === 'undefined' ? null : value);
 	}
 
@@ -59,7 +59,7 @@ class Publisher {
 		this.publish(`driveStream:${userId}`, type, typeof value === 'undefined' ? null : value);
 	}
 
-	public publishNoteStream = <K extends keyof NoteStreamTypes>(noteId: Note['id'], type: K, value: NoteStreamTypes[K]): void => {
+	public publishNoteStream = <K extends keyof NoteStreamTypes>(noteId: Note['id'], type: K, value?: NoteStreamTypes[K]): void => {
 		this.publish(`noteStream:${noteId}`, type, {
 			id: noteId,
 			body: value

From 59580e90937e35a56a7f6b608c09b21ad0dd52c8 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 00:46:38 +0900
Subject: [PATCH 38/91] =?UTF-8?q?user=20pack=E3=81=A8note=20pack=E3=81=AE?=
 =?UTF-8?q?=E5=9E=8B=E4=B8=8D=E6=95=B4=E5=90=88=E3=82=92=E4=BF=AE=E6=AD=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/models/repositories/note.ts | 23 ++++++++++++++++++-----
 src/models/repositories/user.ts | 30 +++++++++---------------------
 2 files changed, 27 insertions(+), 26 deletions(-)

diff --git a/src/models/repositories/note.ts b/src/models/repositories/note.ts
index 77fedd791f..376a09d0c6 100644
--- a/src/models/repositories/note.ts
+++ b/src/models/repositories/note.ts
@@ -447,11 +447,24 @@ export const packedNoteSchema = {
 		channel: {
 			type: 'object' as const,
 			optional: true as const, nullable: true as const,
-			ref: 'Channel' as const,
+			items: {
+				type: 'object' as const,
+				optional: false as const, nullable: false as const,
+				properties: {
+					id: {
+						type: 'string' as const,
+						optional: false as const, nullable: false as const,
+					},
+					name: {
+						type: 'string' as const,
+						optional: false as const, nullable: true as const,
+					},
+				},
+			},
 		},
 		localOnly: {
 			type: 'boolean' as const,
-			optional: false as const, nullable: true as const,
+			optional: true as const, nullable: false as const,
 		},
 		emojis: {
 			type: 'array' as const,
@@ -466,7 +479,7 @@ export const packedNoteSchema = {
 					},
 					url: {
 						type: 'string' as const,
-						optional: false as const, nullable: false as const,
+						optional: false as const, nullable: true as const,
 					},
 				},
 			},
@@ -485,11 +498,11 @@ export const packedNoteSchema = {
 		},
 		uri: {
 			type: 'string' as const,
-			optional: false as const, nullable: true as const,
+			optional: true as const, nullable: false as const,
 		},
 		url: {
 			type: 'string' as const,
-			optional: false as const, nullable: true as const,
+			optional: true as const, nullable: false as const,
 		},
 
 		myReaction: {
diff --git a/src/models/repositories/user.ts b/src/models/repositories/user.ts
index 4a6534b557..39c90cf5ed 100644
--- a/src/models/repositories/user.ts
+++ b/src/models/repositories/user.ts
@@ -375,12 +375,12 @@ export const packedUserSchema = {
 		},
 		isAdmin: {
 			type: 'boolean' as const,
-			nullable: false as const, optional: false as const,
+			nullable: false as const, optional: true as const,
 			default: false
 		},
 		isModerator: {
 			type: 'boolean' as const,
-			nullable: false as const, optional: false as const,
+			nullable: false as const, optional: true as const,
 			default: false
 		},
 		isBot: {
@@ -402,23 +402,11 @@ export const packedUserSchema = {
 						type: 'string' as const,
 						nullable: false as const, optional: false as const
 					},
-					host: {
-						type: 'string' as const,
-						nullable: true as const, optional: false as const
-					},
 					url: {
 						type: 'string' as const,
 						nullable: false as const, optional: false as const,
 						format: 'url'
 					},
-					aliases: {
-						type: 'array' as const,
-						nullable: false as const, optional: false as const,
-						items: {
-							type: 'string' as const,
-							nullable: false as const, optional: false as const
-						}
-					}
 				}
 			}
 		},
@@ -457,7 +445,7 @@ export const packedUserSchema = {
 		},
 		isSuspended: {
 			type: 'boolean' as const,
-			nullable: false as const, optional: false as const,
+			nullable: false as const, optional: true as const,
 			example: false
 		},
 		description: {
@@ -476,7 +464,7 @@ export const packedUserSchema = {
 		},
 		fields: {
 			type: 'array' as const,
-			nullable: false as const, optional: false as const,
+			nullable: false as const, optional: true as const,
 			items: {
 				type: 'object' as const,
 				nullable: false as const, optional: false as const,
@@ -525,26 +513,26 @@ export const packedUserSchema = {
 		},
 		pinnedPageId: {
 			type: 'string' as const,
-			nullable: true as const, optional: false as const
+			nullable: true as const, optional: true as const
 		},
 		pinnedPage: {
 			type: 'object' as const,
-			nullable: true as const, optional: false as const,
+			nullable: true as const, optional: true as const,
 			ref: 'Page' as const,
 		},
 		twoFactorEnabled: {
 			type: 'boolean' as const,
-			nullable: false as const, optional: false as const,
+			nullable: false as const, optional: true as const,
 			default: false
 		},
 		usePasswordLessLogin: {
 			type: 'boolean' as const,
-			nullable: false as const, optional: false as const,
+			nullable: false as const, optional: true as const,
 			default: false
 		},
 		securityKeys: {
 			type: 'boolean' as const,
-			nullable: false as const, optional: false as const,
+			nullable: false as const, optional: true as const,
 			default: false
 		},
 		avatarId: {

From 36e86bbca7ea7881451f3dda24a7dbaf1e4b4168 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 00:47:34 +0900
Subject: [PATCH 39/91] revert
 https://github.com/misskey-dev/misskey/pull/7772#discussion_r706627736

---
 test/utils.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/utils.ts b/test/utils.ts
index 253c410bf0..1a0c54463d 100644
--- a/test/utils.ts
+++ b/test/utils.ts
@@ -158,7 +158,7 @@ export async function initTestDb(justBorrow = false, initEntities?: any[]) {
 		await conn.close();
 	} catch (e) {}
 
-	return createConnection({
+	return await createConnection({
 		type: 'postgres',
 		host: config.db.host,
 		port: config.db.port,

From 94ebf2ac5af89f01bdc3cc64764935e14bf379ce Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 00:47:34 +0900
Subject: [PATCH 40/91] revert
 https://github.com/misskey-dev/misskey/pull/7772#discussion_r706627736

---
 test/utils.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/utils.ts b/test/utils.ts
index 253c410bf0..1a0c54463d 100644
--- a/test/utils.ts
+++ b/test/utils.ts
@@ -158,7 +158,7 @@ export async function initTestDb(justBorrow = false, initEntities?: any[]) {
 		await conn.close();
 	} catch (e) {}
 
-	return createConnection({
+	return await createConnection({
 		type: 'postgres',
 		host: config.db.host,
 		port: config.db.port,

From a7f5ad78b47faaafdd634c450161f6821201f03d Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 00:46:38 +0900
Subject: [PATCH 41/91] =?UTF-8?q?user=20pack=E3=81=A8note=20pack=E3=81=AE?=
 =?UTF-8?q?=E5=9E=8B=E4=B8=8D=E6=95=B4=E5=90=88=E3=82=92=E4=BF=AE=E6=AD=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/models/repositories/note.ts | 23 ++++++++++++++++++-----
 src/models/repositories/user.ts | 30 +++++++++---------------------
 2 files changed, 27 insertions(+), 26 deletions(-)

diff --git a/src/models/repositories/note.ts b/src/models/repositories/note.ts
index 77fedd791f..376a09d0c6 100644
--- a/src/models/repositories/note.ts
+++ b/src/models/repositories/note.ts
@@ -447,11 +447,24 @@ export const packedNoteSchema = {
 		channel: {
 			type: 'object' as const,
 			optional: true as const, nullable: true as const,
-			ref: 'Channel' as const,
+			items: {
+				type: 'object' as const,
+				optional: false as const, nullable: false as const,
+				properties: {
+					id: {
+						type: 'string' as const,
+						optional: false as const, nullable: false as const,
+					},
+					name: {
+						type: 'string' as const,
+						optional: false as const, nullable: true as const,
+					},
+				},
+			},
 		},
 		localOnly: {
 			type: 'boolean' as const,
-			optional: false as const, nullable: true as const,
+			optional: true as const, nullable: false as const,
 		},
 		emojis: {
 			type: 'array' as const,
@@ -466,7 +479,7 @@ export const packedNoteSchema = {
 					},
 					url: {
 						type: 'string' as const,
-						optional: false as const, nullable: false as const,
+						optional: false as const, nullable: true as const,
 					},
 				},
 			},
@@ -485,11 +498,11 @@ export const packedNoteSchema = {
 		},
 		uri: {
 			type: 'string' as const,
-			optional: false as const, nullable: true as const,
+			optional: true as const, nullable: false as const,
 		},
 		url: {
 			type: 'string' as const,
-			optional: false as const, nullable: true as const,
+			optional: true as const, nullable: false as const,
 		},
 
 		myReaction: {
diff --git a/src/models/repositories/user.ts b/src/models/repositories/user.ts
index 4a6534b557..39c90cf5ed 100644
--- a/src/models/repositories/user.ts
+++ b/src/models/repositories/user.ts
@@ -375,12 +375,12 @@ export const packedUserSchema = {
 		},
 		isAdmin: {
 			type: 'boolean' as const,
-			nullable: false as const, optional: false as const,
+			nullable: false as const, optional: true as const,
 			default: false
 		},
 		isModerator: {
 			type: 'boolean' as const,
-			nullable: false as const, optional: false as const,
+			nullable: false as const, optional: true as const,
 			default: false
 		},
 		isBot: {
@@ -402,23 +402,11 @@ export const packedUserSchema = {
 						type: 'string' as const,
 						nullable: false as const, optional: false as const
 					},
-					host: {
-						type: 'string' as const,
-						nullable: true as const, optional: false as const
-					},
 					url: {
 						type: 'string' as const,
 						nullable: false as const, optional: false as const,
 						format: 'url'
 					},
-					aliases: {
-						type: 'array' as const,
-						nullable: false as const, optional: false as const,
-						items: {
-							type: 'string' as const,
-							nullable: false as const, optional: false as const
-						}
-					}
 				}
 			}
 		},
@@ -457,7 +445,7 @@ export const packedUserSchema = {
 		},
 		isSuspended: {
 			type: 'boolean' as const,
-			nullable: false as const, optional: false as const,
+			nullable: false as const, optional: true as const,
 			example: false
 		},
 		description: {
@@ -476,7 +464,7 @@ export const packedUserSchema = {
 		},
 		fields: {
 			type: 'array' as const,
-			nullable: false as const, optional: false as const,
+			nullable: false as const, optional: true as const,
 			items: {
 				type: 'object' as const,
 				nullable: false as const, optional: false as const,
@@ -525,26 +513,26 @@ export const packedUserSchema = {
 		},
 		pinnedPageId: {
 			type: 'string' as const,
-			nullable: true as const, optional: false as const
+			nullable: true as const, optional: true as const
 		},
 		pinnedPage: {
 			type: 'object' as const,
-			nullable: true as const, optional: false as const,
+			nullable: true as const, optional: true as const,
 			ref: 'Page' as const,
 		},
 		twoFactorEnabled: {
 			type: 'boolean' as const,
-			nullable: false as const, optional: false as const,
+			nullable: false as const, optional: true as const,
 			default: false
 		},
 		usePasswordLessLogin: {
 			type: 'boolean' as const,
-			nullable: false as const, optional: false as const,
+			nullable: false as const, optional: true as const,
 			default: false
 		},
 		securityKeys: {
 			type: 'boolean' as const,
-			nullable: false as const, optional: false as const,
+			nullable: false as const, optional: true as const,
 			default: false
 		},
 		avatarId: {

From beac5d2fb791b31a20910a6d9cc39919574489db Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 01:02:56 +0900
Subject: [PATCH 42/91] add prelude/types.ts

---
 src/prelude/types.ts | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 src/prelude/types.ts

diff --git a/src/prelude/types.ts b/src/prelude/types.ts
new file mode 100644
index 0000000000..b8c49f9292
--- /dev/null
+++ b/src/prelude/types.ts
@@ -0,0 +1 @@
+export type Resolved<P> = P extends PromiseLike<infer R> ? Resolved<R> : never;

From 866de46f6533d0bdd0beb0c20fafd65068319e6e Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 01:03:40 +0900
Subject: [PATCH 43/91] emoji

---
 src/models/repositories/emoji.ts | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/models/repositories/emoji.ts b/src/models/repositories/emoji.ts
index c3d7184ec9..2dc5f5282a 100644
--- a/src/models/repositories/emoji.ts
+++ b/src/models/repositories/emoji.ts
@@ -1,5 +1,8 @@
 import { EntityRepository, Repository } from 'typeorm';
 import { Emoji } from '@/models/entities/emoji';
+import { Resolved } from '@/prelude/types';
+
+export type PackedEmoji = Resolved<ReturnType<EmojiRepository['pack']>>;
 
 @EntityRepository(Emoji)
 export class EmojiRepository extends Repository<Emoji> {

From 6df0c45cba666ab59215d46f46a3d3591c603933 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 01:04:07 +0900
Subject: [PATCH 44/91] signin

---
 src/models/repositories/signin.ts | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/models/repositories/signin.ts b/src/models/repositories/signin.ts
index 9942d2d962..8038760742 100644
--- a/src/models/repositories/signin.ts
+++ b/src/models/repositories/signin.ts
@@ -1,5 +1,8 @@
 import { EntityRepository, Repository } from 'typeorm';
 import { Signin } from '@/models/entities/signin';
+import { Resolved } from '@/prelude/types';
+
+export type PackedSignin = Resolved<ReturnType<SigninRepository['pack']>>;
 
 @EntityRepository(Signin)
 export class SigninRepository extends Repository<Signin> {

From 9ea98149573a8653a456fd5b1ed61e70a829ec12 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 01:04:30 +0900
Subject: [PATCH 45/91] game

---
 src/models/repositories/games/reversi/game.ts | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/models/repositories/games/reversi/game.ts b/src/models/repositories/games/reversi/game.ts
index dc91ad51b8..10433bf58f 100644
--- a/src/models/repositories/games/reversi/game.ts
+++ b/src/models/repositories/games/reversi/game.ts
@@ -2,6 +2,9 @@ import { User } from '@/models/entities/user';
 import { EntityRepository, Repository } from 'typeorm';
 import { Users } from '../../../index';
 import { ReversiGame } from '@/models/entities/games/reversi/game';
+import { Resolved } from '@/prelude/types';
+
+export type PackedReversiGame = Resolved<ReturnType<ReversiGameRepository['pack']>>;
 
 @EntityRepository(ReversiGame)
 export class ReversiGameRepository extends Repository<ReversiGame> {

From ace6784b9d00cf06f8097d164d5efb496ac6a36d Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 01:04:55 +0900
Subject: [PATCH 46/91] matching

---
 src/models/repositories/games/reversi/matching.ts | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/models/repositories/games/reversi/matching.ts b/src/models/repositories/games/reversi/matching.ts
index 148221dee5..2696f1f8ea 100644
--- a/src/models/repositories/games/reversi/matching.ts
+++ b/src/models/repositories/games/reversi/matching.ts
@@ -3,6 +3,9 @@ import { ReversiMatching } from '@/models/entities/games/reversi/matching';
 import { Users } from '../../../index';
 import { awaitAll } from '@/prelude/await-all';
 import { User } from '@/models/entities/user';
+import { Resolved } from '@/prelude/types';
+
+export type PackedReversiMatching = Resolved<ReturnType<ReversiMatchingRepository['pack']>>;
 
 @EntityRepository(ReversiMatching)
 export class ReversiMatchingRepository extends Repository<ReversiMatching> {

From 1cc72fdd73acbefb20e35c92139150fd179a89c8 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 01:18:17 +0900
Subject: [PATCH 47/91] clean up

---
 src/server/api/stream/channels/channel.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/server/api/stream/channels/channel.ts b/src/server/api/stream/channels/channel.ts
index a5692563f3..e6a9a6c696 100644
--- a/src/server/api/stream/channels/channel.ts
+++ b/src/server/api/stream/channels/channel.ts
@@ -19,7 +19,7 @@ export default class extends Channel {
 		this.channelId = params.channelId as string;
 
 		// Subscribe stream
-		this.subscriber.on('notesStream', e => this.onNote(e));
+		this.subscriber.on('notesStream', this.onNote);
 		this.subscriber.on(`channelStream:${this.channelId}`, this.onEvent);
 		this.emitTypersIntervalId = setInterval(this.emitTypers, 5000);
 	}

From 8cafc4ee5559664a1e4477e35260f65fcd199a2f Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 01:22:59 +0900
Subject: [PATCH 48/91] ev => data

---
 src/server/api/stream/index.ts | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/src/server/api/stream/index.ts b/src/server/api/stream/index.ts
index 89d9889220..7a980093f7 100644
--- a/src/server/api/stream/index.ts
+++ b/src/server/api/stream/index.ts
@@ -65,36 +65,36 @@ export default class Connection {
 	}
 
 	@autobind
-	private onUserEvent(ev: StreamMessages['user']['spec']) { // { type, body }と展開すると型も展開されてしまう
-		switch (ev.type) {
+	private onUserEvent(data: StreamMessages['user']['spec']) { // { type, body }と展開すると型も展開されてしまう
+		switch (data.type) {
 			case 'follow':
-				this.following.add(ev.body.id);
+				this.following.add(data.body.id);
 				break;
 
 			case 'unfollow':
-				this.following.delete(ev.body.id);
+				this.following.delete(data.body.id);
 				break;
 
 			case 'mute':
-				this.muting.add(ev.body.id);
+				this.muting.add(data.body.id);
 				break;
 
 			case 'unmute':
-				this.muting.delete(ev.body.id);
+				this.muting.delete(data.body.id);
 				break;
 
 			// TODO: block events
 
 			case 'followChannel':
-				this.followingChannels.add(ev.body.id);
+				this.followingChannels.add(data.body.id);
 				break;
 
 			case 'unfollowChannel':
-				this.followingChannels.delete(ev.body.id);
+				this.followingChannels.delete(data.body.id);
 				break;
 
 			case 'updateUserProfile':
-				this.userProfile = ev.body;
+				this.userProfile = data.body;
 				break;
 
 			case 'terminate':

From 380d357a740b4b2d90158b57d1c496f0a5077aa6 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 01:27:20 +0900
Subject: [PATCH 49/91] refactor

---
 src/server/api/stream/index.ts | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/src/server/api/stream/index.ts b/src/server/api/stream/index.ts
index 7a980093f7..ba0a8f6e25 100644
--- a/src/server/api/stream/index.ts
+++ b/src/server/api/stream/index.ts
@@ -47,8 +47,8 @@ export default class Connection {
 
 		this.wsConnection.on('message', this.onWsConnectionMessage);
 
-		this.subscriber.on('broadcast', async ev => {
-			this.onBroadcastMessage(ev.type, ev.body);
+		this.subscriber.on('broadcast', data => {
+			this.onBroadcastMessage(data);
 		});
 
 		if (this.user) {
@@ -58,9 +58,7 @@ export default class Connection {
 			this.updateFollowingChannels();
 			this.updateUserProfile();
 
-			this.subscriber.on(`user:${this.user.id}`, ev => {
-				this.onUserEvent(ev);
-			});
+			this.subscriber.on(`user:${this.user.id}`, this.onUserEvent);
 		}
 	}
 
@@ -146,8 +144,8 @@ export default class Connection {
 	}
 
 	@autobind
-	private onBroadcastMessage(type: string, body: any) {
-		this.sendMessageToWs(type, body);
+	private onBroadcastMessage(data: StreamMessages['broadcast']['spec']) {
+		this.sendMessageToWs(data.type, data.body);
 	}
 
 	@autobind

From 4ba33f24a5e62cc429d3782797ceac898cb58c5c Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 01:28:48 +0900
Subject: [PATCH 50/91] clean up

---
 src/server/api/stream/types.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/server/api/stream/types.ts b/src/server/api/stream/types.ts
index bf67ce90b2..7cb1213f21 100644
--- a/src/server/api/stream/types.ts
+++ b/src/server/api/stream/types.ts
@@ -302,4 +302,4 @@ export type StreamEventEmitter = UnionToIntersection<EventsDictionary[keyof Stre
 // { [y in name]: (e: spec) => void }をまとめてその交差型をEmitterにかけるとts(2590)にひっかかる
 
 // provide stream channels union
-export type StreamChannels = StreamMessages[keyof StreamMessages]['name'] | 'notesStream';
+export type StreamChannels = StreamMessages[keyof StreamMessages]['name'];

From 4bd42cf0710fe7669024c5a26c75112ab297935a Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 01:31:48 +0900
Subject: [PATCH 51/91] add type

---
 src/server/api/stream/index.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/server/api/stream/index.ts b/src/server/api/stream/index.ts
index ba0a8f6e25..5f1aaf0383 100644
--- a/src/server/api/stream/index.ts
+++ b/src/server/api/stream/index.ts
@@ -248,7 +248,7 @@ export default class Connection {
 	}
 
 	@autobind
-	private async onNoteStreamMessage(data: any) {
+	private async onNoteStreamMessage(data: StreamMessages['note']['spec']) {
 		this.sendMessageToWs('noteUpdated', {
 			id: data.body.id,
 			type: data.type,

From 217bf12aab11213e4c1a3891ff71db5c4cafc697 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 01:36:47 +0900
Subject: [PATCH 52/91] antenna

---
 src/server/api/stream/channels/antenna.ts | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/src/server/api/stream/channels/antenna.ts b/src/server/api/stream/channels/antenna.ts
index bf9c53c453..864e42dc66 100644
--- a/src/server/api/stream/channels/antenna.ts
+++ b/src/server/api/stream/channels/antenna.ts
@@ -3,6 +3,7 @@ import Channel from '../channel';
 import { Notes } from '@/models/index';
 import { isMutedUserRelated } from '@/misc/is-muted-user-related';
 import { isBlockerUserRelated } from '@/misc/is-blocker-user-related';
+import { StreamMessages } from '../types';
 
 export default class extends Channel {
 	public readonly chName = 'antenna';
@@ -19,11 +20,9 @@ export default class extends Channel {
 	}
 
 	@autobind
-	private async onEvent(data: any) {
-		const { type, body } = data;
-
-		if (type === 'note') {
-			const note = await Notes.pack(body.id, this.user, { detail: true });
+	private async onEvent(data: StreamMessages['antenna']['spec']) {
+		if (data.type === 'note') {
+			const note = await Notes.pack(data.body.id, this.user, { detail: true });
 
 			// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
 			if (isMutedUserRelated(note, this.muting)) return;
@@ -33,8 +32,6 @@ export default class extends Channel {
 			this.connection.cacheNote(note);
 
 			this.send('note', note);
-		} else {
-			this.send(type, body);
 		}
 	}
 

From 0714c56c413b0849cd88c4637f9d279f603daf28 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 01:37:55 +0900
Subject: [PATCH 53/91] channel

---
 src/server/api/stream/channels/channel.ts | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/server/api/stream/channels/channel.ts b/src/server/api/stream/channels/channel.ts
index e6a9a6c696..e5c78bc695 100644
--- a/src/server/api/stream/channels/channel.ts
+++ b/src/server/api/stream/channels/channel.ts
@@ -5,6 +5,7 @@ import { isMutedUserRelated } from '@/misc/is-muted-user-related';
 import { isBlockerUserRelated } from '@/misc/is-blocker-user-related';
 import { PackedNote } from '@/models/repositories/note';
 import { User } from '@/models/entities/user';
+import { StreamMessages } from '../types';
 
 export default class extends Channel {
 	public readonly chName = 'channel';
@@ -52,7 +53,7 @@ export default class extends Channel {
 	}
 
 	@autobind
-	private onEvent(data: any) {
+	private onEvent(data: StreamMessages['channel']['spec']) {
 		if (data.type === 'typing') {
 			const id = data.body;
 			const begin = this.typers[id] == null;

From c05e99b8f75171ba04b18706f8708aeaef29fc19 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 15:00:48 +0900
Subject: [PATCH 54/91] fix

---
 src/prelude/types.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/prelude/types.ts b/src/prelude/types.ts
index b8c49f9292..0739402479 100644
--- a/src/prelude/types.ts
+++ b/src/prelude/types.ts
@@ -1 +1 @@
-export type Resolved<P> = P extends PromiseLike<infer R> ? Resolved<R> : never;
+export type Resolved<P> = P extends PromiseLike<infer R> ? R : never;

From 04aa4a5e2c8c4b290e7e4bd5c5b89d17b6a8e007 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 15:35:59 +0900
Subject: [PATCH 55/91] add Packed type

---
 src/misc/schema.ts              | 4 +++-
 src/models/repositories/note.ts | 4 ++--
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/misc/schema.ts b/src/misc/schema.ts
index d27c9eff99..52fbbca43b 100644
--- a/src/misc/schema.ts
+++ b/src/misc/schema.ts
@@ -47,6 +47,8 @@ export const refs = {
 	GalleryPost: packedGalleryPostSchema,
 };
 
+export type Packed<x extends keyof typeof refs> = SchemaType<typeof refs[x]>
+
 export interface Schema extends SimpleSchema {
 	items?: Schema;
 	properties?: Obj;
@@ -92,7 +94,7 @@ export type SchemaType<p extends Schema> =
 	p['type'] extends 'array' ? NullOrUndefined<p, MyType<NonNullable<p['items']>>[]> :
 	p['type'] extends 'object' ? (
 		p['ref'] extends keyof typeof refs
-			? NullOrUndefined<p, SchemaType<typeof refs[p['ref']]>>
+			? NullOrUndefined<p, Packed<p['ref']>>
 			: NullOrUndefined<p, ObjType<NonNullable<p['properties']>>>
 	) :
 	p['type'] extends 'any' ? NullOrUndefined<p, any> :
diff --git a/src/models/repositories/note.ts b/src/models/repositories/note.ts
index 376a09d0c6..3a821e9d22 100644
--- a/src/models/repositories/note.ts
+++ b/src/models/repositories/note.ts
@@ -3,14 +3,14 @@ import * as mfm from 'mfm-js';
 import { Note } from '@/models/entities/note';
 import { User } from '@/models/entities/user';
 import { Users, PollVotes, DriveFiles, NoteReactions, Followings, Polls, Channels } from '../index';
-import { SchemaType } from '@/misc/schema';
+import { Packed } from '@/misc/schema';
 import { nyaize } from '@/misc/nyaize';
 import { awaitAll } from '@/prelude/await-all';
 import { convertLegacyReaction, convertLegacyReactions, decodeReaction } from '@/misc/reaction-lib';
 import { NoteReaction } from '@/models/entities/note-reaction';
 import { aggregateNoteEmojis, populateEmojis, prefetchEmojis } from '@/misc/populate-emojis';
 
-export type PackedNote = SchemaType<typeof packedNoteSchema>;
+export type PackedNote = Packed<'Note'>;
 
 @EntityRepository(Note)
 export class NoteRepository extends Repository<Note> {

From e2550e8dddaa9f5524733b962f85c93ea7598051 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 15:48:08 +0900
Subject: [PATCH 56/91] add PackedRef

---
 src/misc/schema.ts | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/misc/schema.ts b/src/misc/schema.ts
index 52fbbca43b..f5e13d76d6 100644
--- a/src/misc/schema.ts
+++ b/src/misc/schema.ts
@@ -47,7 +47,9 @@ export const refs = {
 	GalleryPost: packedGalleryPostSchema,
 };
 
-export type Packed<x extends keyof typeof refs> = SchemaType<typeof refs[x]>
+export type Packed<x extends keyof typeof refs> = PackRef<typeof refs[x]>
+
+type PackRef<s extends { properties: Obj, [x: string]: any }> = { [x in keyof s['properties']]: SchemaType<s['properties'][x]> };
 
 export interface Schema extends SimpleSchema {
 	items?: Schema;

From e777feae9aba5e4c79afaa2a7d4e958da40084da Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 15:53:04 +0900
Subject: [PATCH 57/91] fix lint

---
 src/misc/schema.ts                              |  2 +-
 src/server/api/stream/channels/home-timeline.ts |  3 ++-
 src/server/api/stream/types.ts                  | 11 ++++++-----
 3 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/src/misc/schema.ts b/src/misc/schema.ts
index f5e13d76d6..fbcedbf2ff 100644
--- a/src/misc/schema.ts
+++ b/src/misc/schema.ts
@@ -47,7 +47,7 @@ export const refs = {
 	GalleryPost: packedGalleryPostSchema,
 };
 
-export type Packed<x extends keyof typeof refs> = PackRef<typeof refs[x]>
+export type Packed<x extends keyof typeof refs> = PackRef<typeof refs[x]>;
 
 type PackRef<s extends { properties: Obj, [x: string]: any }> = { [x in keyof s['properties']]: SchemaType<s['properties'][x]> };
 
diff --git a/src/server/api/stream/channels/home-timeline.ts b/src/server/api/stream/channels/home-timeline.ts
index 0e21ab552e..5482ccbfcf 100644
--- a/src/server/api/stream/channels/home-timeline.ts
+++ b/src/server/api/stream/channels/home-timeline.ts
@@ -5,6 +5,7 @@ import { Notes } from '@/models/index';
 import { PackedNote } from '@/models/repositories/note';
 import { checkWordMute } from '@/misc/check-word-mute';
 import { isBlockerUserRelated } from '@/misc/is-blocker-user-related';
+import { Packed } from '@/misc/schema';
 
 export default class extends Channel {
 	public readonly chName = 'homeTimeline';
@@ -18,7 +19,7 @@ export default class extends Channel {
 	}
 
 	@autobind
-	private async onNote(note: PackedNote) {
+	private async onNote(note: Packed<'Note'>) {
 		if (note.channelId) {
 			if (!this.followingChannels.has(note.channelId)) return;
 		} else {
diff --git a/src/server/api/stream/types.ts b/src/server/api/stream/types.ts
index 7cb1213f21..c4cba3d857 100644
--- a/src/server/api/stream/types.ts
+++ b/src/server/api/stream/types.ts
@@ -6,7 +6,7 @@ import { UserProfile } from '@/models/entities/user-profile';
 import { PackedUser } from '@/models/repositories/user';
 import { PackedNotification } from '@/models/repositories/notification';
 import { Note } from '@/models/entities/note';
-import { PackedNote } from '@/models/repositories/note';
+// import { Packed<'Note'> } from '@/models/repositories/note';
 import { Antenna } from '@/models/entities/antenna';
 import { DriveFile } from '@/models/entities/drive-file';
 import { PackedDriveFile } from '@/models/repositories/drive-file';
@@ -24,6 +24,7 @@ import { PackedReversiMatching } from '@/models/repositories/games/reversi/match
 import { AbuseUserReport } from '@/models/entities/abuse-user-report';
 import { PackedSignin } from '@/models/repositories/signin';
 import { Page } from '@/models/entities/page';
+import { Packed } from '@/misc/schema';
 
 // 辞書(interface or type)から{ type, body }ユニオンを定義
 // https://stackoverflow.com/questions/49311989/can-i-infer-the-type-of-a-value-using-extends-keyof-type
@@ -59,9 +60,9 @@ export interface UserStreamTypes {
 
 export interface MainStreamTypes {
 	notification: PackedNotification;
-	mention: PackedNote;
-	reply: PackedNote;
-	renote: PackedNote;
+	mention: Packed<'Note'>;
+	reply: Packed<'Note'>;
+	renote: Packed<'Note'>;
 	follow: PackedUser;
 	followed: PackedUser;
 	unfollow: PackedUser;
@@ -288,7 +289,7 @@ export type StreamMessages = {
 	};
 	notes: {
 		name: 'notesStream';
-		spec: PackedNote;
+		spec: Packed<'Note'>;
 	};
 };
 

From 3afdfe848b1e5f442cd86985adce652ec3c98efb Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 21:11:06 +0900
Subject: [PATCH 58/91] add emoji schema

---
 src/models/repositories/emoji.ts | 44 +++++++++++++++++++++++++++++---
 src/prelude/types.ts             |  1 -
 2 files changed, 41 insertions(+), 4 deletions(-)
 delete mode 100644 src/prelude/types.ts

diff --git a/src/models/repositories/emoji.ts b/src/models/repositories/emoji.ts
index 2dc5f5282a..24f03574fe 100644
--- a/src/models/repositories/emoji.ts
+++ b/src/models/repositories/emoji.ts
@@ -1,14 +1,14 @@
 import { EntityRepository, Repository } from 'typeorm';
 import { Emoji } from '@/models/entities/emoji';
-import { Resolved } from '@/prelude/types';
+import { SchemaType } from '@/misc/schema';
 
-export type PackedEmoji = Resolved<ReturnType<EmojiRepository['pack']>>;
+export type PackedEmoji = SchemaType<typeof packedEmojiSchema>;
 
 @EntityRepository(Emoji)
 export class EmojiRepository extends Repository<Emoji> {
 	public async pack(
 		src: Emoji['id'] | Emoji,
-	) {
+	): Promise<PackedEmoji> {
 		const emoji = typeof src === 'object' ? src : await this.findOneOrFail(src);
 
 		return {
@@ -27,3 +27,41 @@ export class EmojiRepository extends Repository<Emoji> {
 		return Promise.all(emojis.map(x => this.pack(x)));
 	}
 }
+
+export const packedEmojiSchema = {
+	type: 'object' as const,
+	optional: false as const, nullable: false as const,
+	properties: {
+		id: {
+			type: 'string' as const,
+			optional: false as const, nullable: false as const,
+			format: 'id',
+			example: 'xxxxxxxxxx',
+		},
+		aliases: {
+			type: 'array' as const,
+			optional: false as const, nullable: false as const,
+			items: {
+				type: 'string' as const,
+				optional: false as const, nullable: false as const,
+				format: 'id',
+			},
+		},
+		name: {
+			type: 'string' as const,
+			optional: false as const, nullable: false as const,
+		},
+		category: {
+			type: 'string' as const,
+			optional: false as const, nullable: true as const,
+		},
+		host: {
+			type: 'string' as const,
+			optional: false as const, nullable: true as const,
+		},
+		url: {
+			type: 'string' as const,
+			optional: false as const, nullable: false as const,
+		},
+	}
+};
diff --git a/src/prelude/types.ts b/src/prelude/types.ts
deleted file mode 100644
index b8c49f9292..0000000000
--- a/src/prelude/types.ts
+++ /dev/null
@@ -1 +0,0 @@
-export type Resolved<P> = P extends PromiseLike<infer R> ? Resolved<R> : never;

From ce0f3741b9eb7294d67a671b56d80a8ae266a474 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 21:39:02 +0900
Subject: [PATCH 59/91] add reversiGame

---
 src/models/repositories/games/reversi/game.ts | 153 +++++++++++++++++-
 1 file changed, 147 insertions(+), 6 deletions(-)

diff --git a/src/models/repositories/games/reversi/game.ts b/src/models/repositories/games/reversi/game.ts
index 10433bf58f..121a3cad74 100644
--- a/src/models/repositories/games/reversi/game.ts
+++ b/src/models/repositories/games/reversi/game.ts
@@ -2,9 +2,9 @@ import { User } from '@/models/entities/user';
 import { EntityRepository, Repository } from 'typeorm';
 import { Users } from '../../../index';
 import { ReversiGame } from '@/models/entities/games/reversi/game';
-import { Resolved } from '@/prelude/types';
+import { SchemaType } from '@/misc/schema';
 
-export type PackedReversiGame = Resolved<ReturnType<ReversiGameRepository['pack']>>;
+export type PackedReversiGame = SchemaType<typeof packedReversiGameSchema>;
 
 @EntityRepository(ReversiGame)
 export class ReversiGameRepository extends Repository<ReversiGame> {
@@ -14,7 +14,7 @@ export class ReversiGameRepository extends Repository<ReversiGame> {
 		options?: {
 			detail?: boolean
 		}
-	) {
+	): Promise<PackedReversiGame> {
 		const opts = Object.assign({
 			detail: true
 		}, options);
@@ -23,8 +23,8 @@ export class ReversiGameRepository extends Repository<ReversiGame> {
 
 		return {
 			id: game.id,
-			createdAt: game.createdAt,
-			startedAt: game.startedAt,
+			createdAt: game.createdAt.toISOString(),
+			startedAt: game.startedAt && game.startedAt.toISOString(),
 			isStarted: game.isStarted,
 			isEnded: game.isEnded,
 			form1: game.form1,
@@ -44,9 +44,150 @@ export class ReversiGameRepository extends Repository<ReversiGame> {
 			canPutEverywhere: game.canPutEverywhere,
 			loopedBoard: game.loopedBoard,
 			...(opts.detail ? {
-				logs: game.logs,
+				logs: game.logs.map(log => ({
+					at: log.at.toISOString(),
+					color: log.color,
+					pos: log.pos
+				})),
 				map: game.map,
 			} : {})
 		};
 	}
 }
+
+export const packedReversiGameSchema = {
+	type: 'object' as const,
+	optional: false as const, nullable: false as const,
+	properties: {
+		id: {
+			type: 'string' as const,
+			optional: false as const, nullable: false as const,
+			format: 'id',
+			example: 'xxxxxxxxxx',
+		},
+		createdAt: {
+			type: 'string' as const,
+			optional: false as const, nullable: false as const,
+			format: 'date-time',
+		},
+		startedAt: {
+			type: 'string' as const,
+			optional: false as const, nullable: true as const,
+			format: 'date-time',
+		},
+		isStarted: {
+			type: 'boolean' as const,
+			optional: false as const, nullable: false as const,
+		},
+		isEnded: {
+			type: 'boolean' as const,
+			optional: false as const, nullable: false as const,
+		},
+		form1: {
+			type: 'any' as const,
+			optional: false as const, nullable: true as const,
+		},
+		form2: {
+			type: 'any' as const,
+			optional: false as const, nullable: true as const,
+		},
+		user1Accepted: {
+			type: 'boolean' as const,
+			optional: false as const, nullable: false as const,
+		},
+		user2Accepted: {
+			type: 'boolean' as const,
+			optional: false as const, nullable: false as const,
+		},
+		user1Id: {
+			type: 'string' as const,
+			optional: false as const, nullable: false as const,
+			format: 'id',
+			example: 'xxxxxxxxxx',
+		},
+		user2Id: {
+			type: 'string' as const,
+			optional: false as const, nullable: false as const,
+			format: 'id',
+			example: 'xxxxxxxxxx',
+		},
+		user1: {
+			type: 'object' as const,
+			optional: false as const, nullable: false as const,
+			ref: 'User' as const,
+		},
+		user2: {
+			type: 'object' as const,
+			optional: false as const, nullable: false as const,
+			ref: 'User' as const,
+		},
+		winnerId: {
+			type: 'string' as const,
+			optional: false as const, nullable: true as const,
+			format: 'id',
+			example: 'xxxxxxxxxx',
+		},
+		winner: {
+			type: 'object' as const,
+			optional: false as const, nullable: true as const,
+			ref: 'User' as const,
+		},
+		surrendered: {
+			type: 'string' as const,
+			optional: false as const, nullable: true as const,
+			format: 'id',
+			example: 'xxxxxxxxxx',
+		},
+		black: {
+			type: 'number' as const,
+			optional: false as const, nullable: true as const,
+		},
+		bw: {
+			type: 'string' as const,
+			optional: false as const, nullable: false as const,
+		},
+		isLlotheo: {
+			type: 'boolean' as const,
+			optional: false as const, nullable: false as const,
+		},
+		canPutEverywhere: {
+			type: 'boolean' as const,
+			optional: false as const, nullable: false as const,
+		},
+		loopedBoard: {
+			type: 'boolean' as const,
+			optional: false as const, nullable: false as const,
+		},
+		logs: {
+			type: 'array' as const,
+			optional: true as const, nullable: false as const,
+			items: {
+				type: 'object' as const,
+				optional: true as const, nullable: false as const,
+				properties: {
+					at: {
+						type: 'string' as const,
+						optional: false as const, nullable: false as const,
+						format: 'date-time',
+					},
+					color: {
+						type: 'boolean' as const,
+						optional: false as const, nullable: false as const,
+					},
+					pos: {
+						type: 'number' as const,
+						optional: false as const, nullable: false as const,
+					},
+				}
+			}
+		},
+		map: {
+			type: 'array' as const,
+			optional: true as const, nullable: false as const,
+			items: {
+				type: 'string' as const,
+				optional: false as const, nullable: false as const,
+			}
+		}
+	}
+};

From ef98e4ab4f1c14b51af139f97431b50ea55d953e Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 21:44:32 +0900
Subject: [PATCH 60/91] add reversiMatching

---
 .../repositories/games/reversi/matching.ts    | 48 +++++++++++++++++--
 1 file changed, 44 insertions(+), 4 deletions(-)

diff --git a/src/models/repositories/games/reversi/matching.ts b/src/models/repositories/games/reversi/matching.ts
index 2696f1f8ea..0cf3b9fd35 100644
--- a/src/models/repositories/games/reversi/matching.ts
+++ b/src/models/repositories/games/reversi/matching.ts
@@ -3,21 +3,21 @@ import { ReversiMatching } from '@/models/entities/games/reversi/matching';
 import { Users } from '../../../index';
 import { awaitAll } from '@/prelude/await-all';
 import { User } from '@/models/entities/user';
-import { Resolved } from '@/prelude/types';
+import { SchemaType } from '@/misc/schema';
 
-export type PackedReversiMatching = Resolved<ReturnType<ReversiMatchingRepository['pack']>>;
+export type PackedReversiMatching = SchemaType<typeof packedReversiMatchingSchema>;
 
 @EntityRepository(ReversiMatching)
 export class ReversiMatchingRepository extends Repository<ReversiMatching> {
 	public async pack(
 		src: ReversiMatching['id'] | ReversiMatching,
 		me: { id: User['id'] }
-	) {
+	): Promise<PackedReversiMatching> {
 		const matching = typeof src === 'object' ? src : await this.findOneOrFail(src);
 
 		return await awaitAll({
 			id: matching.id,
-			createdAt: matching.createdAt,
+			createdAt: matching.createdAt.toISOString(),
 			parentId: matching.parentId,
 			parent: Users.pack(matching.parentId, me, {
 				detail: true
@@ -29,3 +29,43 @@ export class ReversiMatchingRepository extends Repository<ReversiMatching> {
 		});
 	}
 }
+
+export const packedReversiMatchingSchema = {
+	type: 'object' as const,
+	optional: false as const, nullable: false as const,
+	properties: {
+		id: {
+			type: 'string' as const,
+			optional: false as const, nullable: false as const,
+			format: 'id',
+			example: 'xxxxxxxxxx',
+		},
+		createdAt: {
+			type: 'string' as const,
+			optional: false as const, nullable: false as const,
+			format: 'date-time',
+		},
+		parentId: {
+			type: 'string' as const,
+			optional: false as const, nullable: false as const,
+			format: 'id',
+			example: 'xxxxxxxxxx',
+		},
+		parent: {
+			type: 'object' as const,
+			optional: false as const, nullable: true as const,
+			ref: 'User' as const,
+		},
+		childId: {
+			type: 'string' as const,
+			optional: false as const, nullable: false as const,
+			format: 'id',
+			example: 'xxxxxxxxxx',
+		},
+		child: {
+			type: 'object' as const,
+			optional: false as const, nullable: false as const,
+			ref: 'User' as const,
+		},
+	}
+};

From ee3457cb094cbec4b43261bae9400b4461250856 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 21:46:26 +0900
Subject: [PATCH 61/91] remove signin schema (use Signin entity)

---
 src/models/repositories/signin.ts | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/src/models/repositories/signin.ts b/src/models/repositories/signin.ts
index 8038760742..9942d2d962 100644
--- a/src/models/repositories/signin.ts
+++ b/src/models/repositories/signin.ts
@@ -1,8 +1,5 @@
 import { EntityRepository, Repository } from 'typeorm';
 import { Signin } from '@/models/entities/signin';
-import { Resolved } from '@/prelude/types';
-
-export type PackedSignin = Resolved<ReturnType<SigninRepository['pack']>>;
 
 @EntityRepository(Signin)
 export class SigninRepository extends Repository<Signin> {

From e749b756a0d54085d25f0b93fc4886f8f665c26f Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 22:12:48 +0900
Subject: [PATCH 62/91] add schemas refs, fix Packed type

---
 src/misc/schema.ts | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/src/misc/schema.ts b/src/misc/schema.ts
index fbcedbf2ff..4131875ef7 100644
--- a/src/misc/schema.ts
+++ b/src/misc/schema.ts
@@ -21,6 +21,9 @@ import { packedClipSchema } from '@/models/repositories/clip';
 import { packedFederationInstanceSchema } from '@/models/repositories/federation-instance';
 import { packedQueueCountSchema } from '@/models/repositories/queue';
 import { packedGalleryPostSchema } from '@/models/repositories/gallery-post';
+import { packedEmojiSchema } from '@/models/repositories/emoji';
+import { packedReversiGameSchema } from '@/models/repositories/games/reversi/game';
+import { packedReversiMatchingSchema } from '@/models/repositories/games/reversi/matching';
 
 export const refs = {
 	User: packedUserSchema,
@@ -45,11 +48,12 @@ export const refs = {
 	Clip: packedClipSchema,
 	FederationInstance: packedFederationInstanceSchema,
 	GalleryPost: packedGalleryPostSchema,
+	Emoji: packedEmojiSchema,
+	ReversiGame: packedReversiGameSchema,
+	ReversiMatching: packedReversiMatchingSchema,
 };
 
-export type Packed<x extends keyof typeof refs> = PackRef<typeof refs[x]>;
-
-type PackRef<s extends { properties: Obj, [x: string]: any }> = { [x in keyof s['properties']]: SchemaType<s['properties'][x]> };
+export type Packed<x extends keyof typeof refs> = ObjType<(typeof refs[x])['properties']>;
 
 export interface Schema extends SimpleSchema {
 	items?: Schema;

From dee40b33d25008f2642f15e99f27b44534401dae Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 22:18:26 +0900
Subject: [PATCH 63/91] wip PackedHoge => Packed<'Hoge'>

---
 src/server/api/stream/types.ts | 71 +++++++++++++++-------------------
 1 file changed, 31 insertions(+), 40 deletions(-)

diff --git a/src/server/api/stream/types.ts b/src/server/api/stream/types.ts
index c4cba3d857..b3a2df0020 100644
--- a/src/server/api/stream/types.ts
+++ b/src/server/api/stream/types.ts
@@ -3,26 +3,17 @@ import Emitter from 'strict-event-emitter-types';
 import { Channel } from '@/models/entities/channel';
 import { User } from '@/models/entities/user';
 import { UserProfile } from '@/models/entities/user-profile';
-import { PackedUser } from '@/models/repositories/user';
-import { PackedNotification } from '@/models/repositories/notification';
 import { Note } from '@/models/entities/note';
-// import { Packed<'Note'> } from '@/models/repositories/note';
 import { Antenna } from '@/models/entities/antenna';
 import { DriveFile } from '@/models/entities/drive-file';
-import { PackedDriveFile } from '@/models/repositories/drive-file';
 import { DriveFolder } from '@/models/entities/drive-folder';
-import { PackedDriveFolder } from '@/models/repositories/drive-folder';
 import { Emoji } from '@/models/entities/emoji';
-import { PackedEmoji } from '@/models/repositories/emoji';
 import { UserList } from '@/models/entities/user-list';
 import { MessagingMessage } from '@/models/entities/messaging-message';
-import { PackedMessagingMessage } from '@/models/repositories/messaging-message';
 import { UserGroup } from '@/models/entities/user-group';
 import { ReversiGame } from '@/models/entities/games/reversi/game';
-import { PackedReversiGame } from '@/models/repositories/games/reversi/game';
-import { PackedReversiMatching } from '@/models/repositories/games/reversi/matching';
 import { AbuseUserReport } from '@/models/entities/abuse-user-report';
-import { PackedSignin } from '@/models/repositories/signin';
+import { Signin } from '@/models/entities/signin';
 import { Page } from '@/models/entities/page';
 import { Packed } from '@/misc/schema';
 
@@ -42,7 +33,7 @@ export interface InternalStreamTypes {
 
 export interface BroadcastTypes {
 	emojiAdded: {
-		emoji: PackedEmoji;
+		emoji: Packed<'Emoji'>;
 	};
 }
 
@@ -53,40 +44,40 @@ export interface UserStreamTypes {
 	updateUserProfile: UserProfile;
 	mute: User;
 	unmute: User;
-	follow: PackedUser;
-	unfollow: PackedUser;
-	userAdded: PackedUser;
+	follow: Packed<'User'>;
+	unfollow: Packed<'User'>;
+	userAdded: Packed<'User'>;
 }
 
 export interface MainStreamTypes {
-	notification: PackedNotification;
+	notification: Packed<'Notification'>;
 	mention: Packed<'Note'>;
 	reply: Packed<'Note'>;
 	renote: Packed<'Note'>;
-	follow: PackedUser;
-	followed: PackedUser;
-	unfollow: PackedUser;
-	meUpdated: PackedUser;
+	follow: Packed<'User'>;
+	followed: Packed<'User'>;
+	unfollow: Packed<'User'>;
+	meUpdated: Packed<'User'>;
 	pageEvent: {
 		pageId: Page['id'];
 		event: string;
 		var: any;
 		userId: User['id'];
-		user: PackedUser;
+		user: Packed<'User'>;
 	};
 	urlUploadFinished: {
 		marker?: string | null;
-		file: PackedDriveFile;
+		file: Packed<'DriveFile'>;
 	};
 	readAllNotifications: undefined;
-	unreadNotification: PackedNotification;
+	unreadNotification: Packed<'Notification'>;
 	unreadMention: Note['id'];
 	readAllUnreadMentions: undefined;
 	unreadSpecifiedNote: Note['id'];
 	readAllUnreadSpecifiedNotes: undefined;
 	readAllMessagingMessages: undefined;
-	messagingMessage: PackedMessagingMessage;
-	unreadMessagingMessage: PackedMessagingMessage;
+	messagingMessage: Packed<'MessagingMessage'>;
+	unreadMessagingMessage: Packed<'MessagingMessage'>;
 	readAllAntennas: undefined;
 	unreadAntenna: Antenna;
 	readAllAnnouncements: undefined;
@@ -94,24 +85,24 @@ export interface MainStreamTypes {
 	unreadChannel: Note['id'];
 	myTokenRegenerated: undefined;
 	reversiNoInvites: undefined;
-	reversiInvited: PackedReversiMatching;
-	signin: PackedSignin;
+	reversiInvited: Packed<'ReversiMatching'>;
+	signin: Signin;
 	registryUpdated: {
 		scope?: string[];
 		key: string;
 		value: any | null;
 	};
-	driveFileCreated: PackedDriveFile;
+	driveFileCreated: Packed<'DriveFile'>;
 	readAntenna: Antenna;
 }
 
 export interface DriveStreamTypes {
-	fileCreated: PackedDriveFile;
+	fileCreated: Packed<'DriveFile'>;
 	fileDeleted: DriveFile['id'];
-	fileUpdated: PackedDriveFile;
-	folderCreated: PackedDriveFolder;
+	fileUpdated: Packed<'DriveFile'>;
+	folderCreated: Packed<'DriveFolder'>;
 	folderDeleted: DriveFolder['id'];
-	folderUpdated: PackedDriveFolder;
+	folderUpdated: Packed<'DriveFolder'>;
 }
 
 export interface NoteStreamTypes {
@@ -144,8 +135,8 @@ export interface ChannelStreamTypes {
 }
 
 export interface UserListStreamTypes {
-	userAdded: PackedUser;
-	userRemoved: PackedUser;
+	userAdded: Packed<'User'>;
+	userRemoved: Packed<'User'>;
 }
 
 export interface AntennaStreamTypes {
@@ -155,7 +146,7 @@ export interface AntennaStreamTypes {
 export interface MessagingStreamTypes {
 	read: MessagingMessage['id'][];
 	typing: User['id'];
-	message: PackedMessagingMessage;
+	message: Packed<'MessagingMessage'>;
 	deleted: MessagingMessage['id'];
 }
 
@@ -165,25 +156,25 @@ export interface GroupMessagingStreamTypes {
 		userId: User['id'];
 	};
 	typing: User['id'];
-	message: PackedMessagingMessage;
+	message: Packed<'MessagingMessage'>;
 	deleted: MessagingMessage['id'];
 }
 
 export interface MessagingIndexStreamTypes {
 	read: MessagingMessage['id'][];
-	message: PackedMessagingMessage;
+	message: Packed<'MessagingMessage'>;
 }
 
 export interface ReversiStreamTypes {
-	matched: PackedReversiGame;
-	invited: PackedReversiMatching;
+	matched: Packed<'ReversiGame'>;
+	invited: Packed<'ReversiMatching'>;
 }
 
 export interface ReversiGameStreamTypes {
-	started: PackedReversiGame;
+	started: Packed<'ReversiGame'>;
 	ended: {
 		winnerId?: User['id'] | null,
-		game: PackedReversiGame;
+		game: Packed<'ReversiGame'>;
 	};
 	updateSettings: {
 		key: string;

From a97fb08af09fb91ee7a159c827ab6b60047860ed Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 22:20:21 +0900
Subject: [PATCH 64/91] add Packed type

---
 src/misc/schema.ts | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/misc/schema.ts b/src/misc/schema.ts
index d27c9eff99..4131875ef7 100644
--- a/src/misc/schema.ts
+++ b/src/misc/schema.ts
@@ -21,6 +21,9 @@ import { packedClipSchema } from '@/models/repositories/clip';
 import { packedFederationInstanceSchema } from '@/models/repositories/federation-instance';
 import { packedQueueCountSchema } from '@/models/repositories/queue';
 import { packedGalleryPostSchema } from '@/models/repositories/gallery-post';
+import { packedEmojiSchema } from '@/models/repositories/emoji';
+import { packedReversiGameSchema } from '@/models/repositories/games/reversi/game';
+import { packedReversiMatchingSchema } from '@/models/repositories/games/reversi/matching';
 
 export const refs = {
 	User: packedUserSchema,
@@ -45,8 +48,13 @@ export const refs = {
 	Clip: packedClipSchema,
 	FederationInstance: packedFederationInstanceSchema,
 	GalleryPost: packedGalleryPostSchema,
+	Emoji: packedEmojiSchema,
+	ReversiGame: packedReversiGameSchema,
+	ReversiMatching: packedReversiMatchingSchema,
 };
 
+export type Packed<x extends keyof typeof refs> = ObjType<(typeof refs[x])['properties']>;
+
 export interface Schema extends SimpleSchema {
 	items?: Schema;
 	properties?: Obj;
@@ -92,7 +100,7 @@ export type SchemaType<p extends Schema> =
 	p['type'] extends 'array' ? NullOrUndefined<p, MyType<NonNullable<p['items']>>[]> :
 	p['type'] extends 'object' ? (
 		p['ref'] extends keyof typeof refs
-			? NullOrUndefined<p, SchemaType<typeof refs[p['ref']]>>
+			? NullOrUndefined<p, Packed<p['ref']>>
 			: NullOrUndefined<p, ObjType<NonNullable<p['properties']>>>
 	) :
 	p['type'] extends 'any' ? NullOrUndefined<p, any> :

From 1f82a983848108a9ce44e5280837886ed00e45c1 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 22:34:27 +0900
Subject: [PATCH 65/91] note-reaction

---
 src/models/repositories/note-reaction.ts | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/models/repositories/note-reaction.ts b/src/models/repositories/note-reaction.ts
index e73a832109..ba74076f6c 100644
--- a/src/models/repositories/note-reaction.ts
+++ b/src/models/repositories/note-reaction.ts
@@ -1,18 +1,16 @@
 import { EntityRepository, Repository } from 'typeorm';
 import { NoteReaction } from '@/models/entities/note-reaction';
 import { Users } from '../index';
-import { SchemaType } from '@/misc/schema';
+import { Packed } from '@/misc/schema';
 import { convertLegacyReaction } from '@/misc/reaction-lib';
 import { User } from '@/models/entities/user';
 
-export type PackedNoteReaction = SchemaType<typeof packedNoteReactionSchema>;
-
 @EntityRepository(NoteReaction)
 export class NoteReactionRepository extends Repository<NoteReaction> {
 	public async pack(
 		src: NoteReaction['id'] | NoteReaction,
 		me?: { id: User['id'] } | null | undefined
-	): Promise<PackedNoteReaction> {
+	): Promise<Packed<'NoteReaction'>> {
 		const reaction = typeof src === 'object' ? src : await this.findOneOrFail(src);
 
 		return {

From d7488715df4a956b52ee25a8350ea31a5e7978d8 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 22:40:18 +0900
Subject: [PATCH 66/91] user

---
 src/models/repositories/user.ts | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/models/repositories/user.ts b/src/models/repositories/user.ts
index 39c90cf5ed..b6f27e32e2 100644
--- a/src/models/repositories/user.ts
+++ b/src/models/repositories/user.ts
@@ -3,14 +3,12 @@ import { EntityRepository, Repository, In, Not } from 'typeorm';
 import { User, ILocalUser, IRemoteUser } from '@/models/entities/user';
 import { Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles, UserSecurityKeys, UserGroupJoinings, Pages, Announcements, AnnouncementReads, Antennas, AntennaNotes, ChannelFollowings, Instances } from '../index';
 import config from '@/config/index';
-import { SchemaType } from '@/misc/schema';
+import { Packed } from '@/misc/schema';
 import { awaitAll } from '@/prelude/await-all';
 import { populateEmojis } from '@/misc/populate-emojis';
 import { getAntennas } from '@/misc/antenna-cache';
 import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const';
 
-export type PackedUser = SchemaType<typeof packedUserSchema>;
-
 @EntityRepository(User)
 export class UserRepository extends Repository<User> {
 	public async getRelation(me: User['id'], target: User['id']) {
@@ -164,7 +162,7 @@ export class UserRepository extends Repository<User> {
 			detail?: boolean,
 			includeSecrets?: boolean,
 		}
-	): Promise<PackedUser> {
+	): Promise<Packed<'User'>> {
 		const opts = Object.assign({
 			detail: false,
 			includeSecrets: false

From 3eb79b23bcc23742919f9ad88e7926b4a5a5f15a Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 22:40:51 +0900
Subject: [PATCH 67/91] user-group

---
 src/models/repositories/user-group.ts | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/models/repositories/user-group.ts b/src/models/repositories/user-group.ts
index a76ac7b9d3..b38a2fb50d 100644
--- a/src/models/repositories/user-group.ts
+++ b/src/models/repositories/user-group.ts
@@ -1,15 +1,13 @@
 import { EntityRepository, Repository } from 'typeorm';
 import { UserGroup } from '@/models/entities/user-group';
 import { UserGroupJoinings } from '../index';
-import { SchemaType } from '@/misc/schema';
-
-export type PackedUserGroup = SchemaType<typeof packedUserGroupSchema>;
+import { Packed } from '@/misc/schema';
 
 @EntityRepository(UserGroup)
 export class UserGroupRepository extends Repository<UserGroup> {
 	public async pack(
 		src: UserGroup['id'] | UserGroup,
-	): Promise<PackedUserGroup> {
+	): Promise<Packed<'UserGroup'>> {
 		const userGroup = typeof src === 'object' ? src : await this.findOneOrFail(src);
 
 		const users = await UserGroupJoinings.find({

From c9926414a0dcca06b7dc7c9deffdaedf52fc0c48 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 22:41:38 +0900
Subject: [PATCH 68/91] user-list

---
 src/models/repositories/user-list.ts | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/models/repositories/user-list.ts b/src/models/repositories/user-list.ts
index 809dbe0268..331c278e6f 100644
--- a/src/models/repositories/user-list.ts
+++ b/src/models/repositories/user-list.ts
@@ -1,15 +1,13 @@
 import { EntityRepository, Repository } from 'typeorm';
 import { UserList } from '@/models/entities/user-list';
 import { UserListJoinings } from '../index';
-import { SchemaType } from '@/misc/schema';
-
-export type PackedUserList = SchemaType<typeof packedUserListSchema>;
+import { Packed } from '@/misc/schema';
 
 @EntityRepository(UserList)
 export class UserListRepository extends Repository<UserList> {
 	public async pack(
 		src: UserList['id'] | UserList,
-	): Promise<PackedUserList> {
+	): Promise<Packed<'UserList'>> {
 		const userList = typeof src === 'object' ? src : await this.findOneOrFail(src);
 
 		const users = await UserListJoinings.find({

From 677a6aae2afe13417d26f4dc47b3f7078b4cfc52 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 22:47:03 +0900
Subject: [PATCH 69/91] note

---
 src/misc/check-hit-antenna.ts                     |  4 ++--
 src/models/repositories/note.ts                   |  8 +++-----
 src/server/api/stream/channels/channel.ts         |  4 ++--
 src/server/api/stream/channels/global-timeline.ts |  4 ++--
 src/server/api/stream/channels/hashtag.ts         |  4 ++--
 src/server/api/stream/channels/home-timeline.ts   |  4 ++--
 src/server/api/stream/channels/hybrid-timeline.ts |  4 ++--
 src/server/api/stream/channels/local-timeline.ts  |  4 ++--
 src/server/api/stream/channels/user-list.ts       |  4 ++--
 src/server/api/stream/index.ts                    |  8 ++++----
 src/services/note/read.ts                         | 14 +++++++-------
 11 files changed, 30 insertions(+), 32 deletions(-)

diff --git a/src/misc/check-hit-antenna.ts b/src/misc/check-hit-antenna.ts
index 38965f4b0d..3789054b26 100644
--- a/src/misc/check-hit-antenna.ts
+++ b/src/misc/check-hit-antenna.ts
@@ -3,13 +3,13 @@ import { Note } from '@/models/entities/note';
 import { User } from '@/models/entities/user';
 import { UserListJoinings, UserGroupJoinings } from '@/models/index';
 import { getFullApAccount } from './convert-host';
-import { PackedNote } from '../models/repositories/note';
 import { parseAcct } from '@/misc/acct';
+import { Packed } from './schema';
 
 /**
  * noteUserFollowers / antennaUserFollowing はどちらか一方が指定されていればよい
  */
-export async function checkHitAntenna(antenna: Antenna, note: (Note | PackedNote), noteUser: { username: string; host: string | null; }, noteUserFollowers?: User['id'][], antennaUserFollowing?: User['id'][]): Promise<boolean> {
+export async function checkHitAntenna(antenna: Antenna, note: (Note | Packed<'Note'>), noteUser: { username: string; host: string | null; }, noteUserFollowers?: User['id'][], antennaUserFollowing?: User['id'][]): Promise<boolean> {
 	if (note.visibility === 'specified') return false;
 
 	if (note.visibility === 'followers') {
diff --git a/src/models/repositories/note.ts b/src/models/repositories/note.ts
index 376a09d0c6..c0ac22b2db 100644
--- a/src/models/repositories/note.ts
+++ b/src/models/repositories/note.ts
@@ -3,15 +3,13 @@ import * as mfm from 'mfm-js';
 import { Note } from '@/models/entities/note';
 import { User } from '@/models/entities/user';
 import { Users, PollVotes, DriveFiles, NoteReactions, Followings, Polls, Channels } from '../index';
-import { SchemaType } from '@/misc/schema';
+import { Packed } from '@/misc/schema';
 import { nyaize } from '@/misc/nyaize';
 import { awaitAll } from '@/prelude/await-all';
 import { convertLegacyReaction, convertLegacyReactions, decodeReaction } from '@/misc/reaction-lib';
 import { NoteReaction } from '@/models/entities/note-reaction';
 import { aggregateNoteEmojis, populateEmojis, prefetchEmojis } from '@/misc/populate-emojis';
 
-export type PackedNote = SchemaType<typeof packedNoteSchema>;
-
 @EntityRepository(Note)
 export class NoteRepository extends Repository<Note> {
 	public validateCw(x: string) {
@@ -67,7 +65,7 @@ export class NoteRepository extends Repository<Note> {
 		return true;
 	}
 
-	private async hideNote(packedNote: PackedNote, meId: User['id'] | null) {
+	private async hideNote(packedNote: Packed<'Note'>, meId: User['id'] | null) {
 		// TODO: isVisibleForMe を使うようにしても良さそう(型違うけど)
 		let hide = false;
 
@@ -137,7 +135,7 @@ export class NoteRepository extends Repository<Note> {
 				myReactions: Map<Note['id'], NoteReaction | null>;
 			};
 		}
-	): Promise<PackedNote> {
+	): Promise<Packed<'Note'>> {
 		const opts = Object.assign({
 			detail: true,
 			skipHide: false
diff --git a/src/server/api/stream/channels/channel.ts b/src/server/api/stream/channels/channel.ts
index e6a9a6c696..72ddbf93b4 100644
--- a/src/server/api/stream/channels/channel.ts
+++ b/src/server/api/stream/channels/channel.ts
@@ -3,8 +3,8 @@ import Channel from '../channel';
 import { Notes, Users } from '@/models/index';
 import { isMutedUserRelated } from '@/misc/is-muted-user-related';
 import { isBlockerUserRelated } from '@/misc/is-blocker-user-related';
-import { PackedNote } from '@/models/repositories/note';
 import { User } from '@/models/entities/user';
+import { Packed } from '@/misc/schema';
 
 export default class extends Channel {
 	public readonly chName = 'channel';
@@ -25,7 +25,7 @@ export default class extends Channel {
 	}
 
 	@autobind
-	private async onNote(note: PackedNote) {
+	private async onNote(note: Packed<'Note'>) {
 		if (note.channelId !== this.channelId) return;
 
 		// リプライなら再pack
diff --git a/src/server/api/stream/channels/global-timeline.ts b/src/server/api/stream/channels/global-timeline.ts
index 384ed61409..f5983ab472 100644
--- a/src/server/api/stream/channels/global-timeline.ts
+++ b/src/server/api/stream/channels/global-timeline.ts
@@ -3,9 +3,9 @@ import { isMutedUserRelated } from '@/misc/is-muted-user-related';
 import Channel from '../channel';
 import { fetchMeta } from '@/misc/fetch-meta';
 import { Notes } from '@/models/index';
-import { PackedNote } from '@/models/repositories/note';
 import { checkWordMute } from '@/misc/check-word-mute';
 import { isBlockerUserRelated } from '@/misc/is-blocker-user-related';
+import { Packed } from '@/misc/schema';
 
 export default class extends Channel {
 	public readonly chName = 'globalTimeline';
@@ -24,7 +24,7 @@ export default class extends Channel {
 	}
 
 	@autobind
-	private async onNote(note: PackedNote) {
+	private async onNote(note: Packed<'Note'>) {
 		if (note.visibility !== 'public') return;
 		if (note.channelId != null) return;
 
diff --git a/src/server/api/stream/channels/hashtag.ts b/src/server/api/stream/channels/hashtag.ts
index 997ab75f6d..281be4f2eb 100644
--- a/src/server/api/stream/channels/hashtag.ts
+++ b/src/server/api/stream/channels/hashtag.ts
@@ -2,9 +2,9 @@ import autobind from 'autobind-decorator';
 import { isMutedUserRelated } from '@/misc/is-muted-user-related';
 import Channel from '../channel';
 import { Notes } from '@/models/index';
-import { PackedNote } from '@/models/repositories/note';
 import { normalizeForSearch } from '@/misc/normalize-for-search';
 import { isBlockerUserRelated } from '@/misc/is-blocker-user-related';
+import { Packed } from '@/misc/schema';
 
 export default class extends Channel {
 	public readonly chName = 'hashtag';
@@ -23,7 +23,7 @@ export default class extends Channel {
 	}
 
 	@autobind
-	private async onNote(note: PackedNote) {
+	private async onNote(note: Packed<'Note'>) {
 		const noteTags = note.tags ? note.tags.map((t: string) => t.toLowerCase()) : [];
 		const matched = this.q.some(tags => tags.every(tag => noteTags.includes(normalizeForSearch(tag))));
 		if (!matched) return;
diff --git a/src/server/api/stream/channels/home-timeline.ts b/src/server/api/stream/channels/home-timeline.ts
index 0e21ab552e..52e9aec250 100644
--- a/src/server/api/stream/channels/home-timeline.ts
+++ b/src/server/api/stream/channels/home-timeline.ts
@@ -2,9 +2,9 @@ import autobind from 'autobind-decorator';
 import { isMutedUserRelated } from '@/misc/is-muted-user-related';
 import Channel from '../channel';
 import { Notes } from '@/models/index';
-import { PackedNote } from '@/models/repositories/note';
 import { checkWordMute } from '@/misc/check-word-mute';
 import { isBlockerUserRelated } from '@/misc/is-blocker-user-related';
+import { Packed } from '@/misc/schema';
 
 export default class extends Channel {
 	public readonly chName = 'homeTimeline';
@@ -18,7 +18,7 @@ export default class extends Channel {
 	}
 
 	@autobind
-	private async onNote(note: PackedNote) {
+	private async onNote(note: Packed<'Note'>) {
 		if (note.channelId) {
 			if (!this.followingChannels.has(note.channelId)) return;
 		} else {
diff --git a/src/server/api/stream/channels/hybrid-timeline.ts b/src/server/api/stream/channels/hybrid-timeline.ts
index 0b28ff616b..51f95fc0cd 100644
--- a/src/server/api/stream/channels/hybrid-timeline.ts
+++ b/src/server/api/stream/channels/hybrid-timeline.ts
@@ -3,9 +3,9 @@ import { isMutedUserRelated } from '@/misc/is-muted-user-related';
 import Channel from '../channel';
 import { fetchMeta } from '@/misc/fetch-meta';
 import { Notes } from '@/models/index';
-import { PackedNote } from '@/models/repositories/note';
 import { checkWordMute } from '@/misc/check-word-mute';
 import { isBlockerUserRelated } from '@/misc/is-blocker-user-related';
+import { Packed } from '@/misc/schema';
 
 export default class extends Channel {
 	public readonly chName = 'hybridTimeline';
@@ -22,7 +22,7 @@ export default class extends Channel {
 	}
 
 	@autobind
-	private async onNote(note: PackedNote) {
+	private async onNote(note: Packed<'Note'>) {
 		// チャンネルの投稿ではなく、自分自身の投稿 または
 		// チャンネルの投稿ではなく、その投稿のユーザーをフォローしている または
 		// チャンネルの投稿ではなく、全体公開のローカルの投稿 または
diff --git a/src/server/api/stream/channels/local-timeline.ts b/src/server/api/stream/channels/local-timeline.ts
index 20061410c4..a6166c2be2 100644
--- a/src/server/api/stream/channels/local-timeline.ts
+++ b/src/server/api/stream/channels/local-timeline.ts
@@ -3,9 +3,9 @@ import { isMutedUserRelated } from '@/misc/is-muted-user-related';
 import Channel from '../channel';
 import { fetchMeta } from '@/misc/fetch-meta';
 import { Notes } from '@/models/index';
-import { PackedNote } from '@/models/repositories/note';
 import { checkWordMute } from '@/misc/check-word-mute';
 import { isBlockerUserRelated } from '@/misc/is-blocker-user-related';
+import { Packed } from '@/misc/schema';
 
 export default class extends Channel {
 	public readonly chName = 'localTimeline';
@@ -24,7 +24,7 @@ export default class extends Channel {
 	}
 
 	@autobind
-	private async onNote(note: PackedNote) {
+	private async onNote(note: Packed<'Note'>) {
 		if (note.user.host !== null) return;
 		if (note.visibility !== 'public') return;
 		if (note.channelId != null && !this.followingChannels.has(note.channelId)) return;
diff --git a/src/server/api/stream/channels/user-list.ts b/src/server/api/stream/channels/user-list.ts
index 0ca83cd658..63b254605b 100644
--- a/src/server/api/stream/channels/user-list.ts
+++ b/src/server/api/stream/channels/user-list.ts
@@ -3,8 +3,8 @@ import Channel from '../channel';
 import { Notes, UserListJoinings, UserLists } from '@/models/index';
 import { isMutedUserRelated } from '@/misc/is-muted-user-related';
 import { User } from '@/models/entities/user';
-import { PackedNote } from '@/models/repositories/note';
 import { isBlockerUserRelated } from '@/misc/is-blocker-user-related';
+import { Packed } from '@/misc/schema';
 
 export default class extends Channel {
 	public readonly chName = 'userList';
@@ -47,7 +47,7 @@ export default class extends Channel {
 	}
 
 	@autobind
-	private async onNote(note: PackedNote) {
+	private async onNote(note: Packed<'Note'>) {
 		if (!this.listUsers.includes(note.userId)) return;
 
 		if (['followers', 'specified'].includes(note.visibility)) {
diff --git a/src/server/api/stream/index.ts b/src/server/api/stream/index.ts
index f83bc9331e..ccd555e149 100644
--- a/src/server/api/stream/index.ts
+++ b/src/server/api/stream/index.ts
@@ -14,7 +14,7 @@ import { AccessToken } from '@/models/entities/access-token';
 import { UserProfile } from '@/models/entities/user-profile';
 import { publishChannelStream, publishGroupMessagingStream, publishMessagingStream } from '@/services/stream';
 import { UserGroup } from '@/models/entities/user-group';
-import { PackedNote } from '@/models/repositories/note';
+import { Packed } from '@/misc/schema';
 
 /**
  * Main stream connection
@@ -31,7 +31,7 @@ export default class Connection {
 	public subscriber: EventEmitter;
 	private channels: Channel[] = [];
 	private subscribingNotes: any = {};
-	private cachedNotes: PackedNote[] = [];
+	private cachedNotes: Packed<'Note'>[] = [];
 
 	constructor(
 		wsConnection: websocket.connection,
@@ -150,8 +150,8 @@ export default class Connection {
 	}
 
 	@autobind
-	public cacheNote(note: PackedNote) {
-		const add = (note: PackedNote) => {
+	public cacheNote(note: Packed<'Note'>) {
+		const add = (note: Packed<'Note'>) => {
 			const existIndex = this.cachedNotes.findIndex(n => n.id === note.id);
 			if (existIndex > -1) {
 				this.cachedNotes[existIndex] = note;
diff --git a/src/services/note/read.ts b/src/services/note/read.ts
index 2e221d553a..f25f86da9c 100644
--- a/src/services/note/read.ts
+++ b/src/services/note/read.ts
@@ -6,15 +6,15 @@ import { Not, IsNull, In } from 'typeorm';
 import { Channel } from '@/models/entities/channel';
 import { checkHitAntenna } from '@/misc/check-hit-antenna';
 import { getAntennas } from '@/misc/antenna-cache';
-import { PackedNote } from '@/models/repositories/note';
 import { readNotificationByQuery } from '@/server/api/common/read-notification';
+import { Packed } from '@/misc/schema';
 
 /**
  * Mark notes as read
  */
 export default async function(
 	userId: User['id'],
-	notes: (Note | PackedNote)[],
+	notes: (Note | Packed<'Note'>)[],
 	info?: {
 		following: Set<User['id']>;
 		followingChannels: Set<Channel['id']>;
@@ -34,10 +34,10 @@ export default async function(
 	})).map(x => x.followeeId));
 
 	const myAntennas = (await getAntennas()).filter(a => a.userId === userId);
-	const readMentions: (Note | PackedNote)[] = [];
-	const readSpecifiedNotes: (Note | PackedNote)[] = [];
-	const readChannelNotes: (Note | PackedNote)[] = [];
-	const readAntennaNotes: (Note | PackedNote)[] = [];
+	const readMentions: (Note | Packed<'Note'>)[] = [];
+	const readSpecifiedNotes: (Note | Packed<'Note'>)[] = [];
+	const readChannelNotes: (Note | Packed<'Note'>)[] = [];
+	const readAntennaNotes: (Note | Packed<'Note'>)[] = [];
 
 	for (const note of notes) {
 		if (note.mentions && note.mentions.includes(userId)) {
@@ -52,7 +52,7 @@ export default async function(
 
 		if (note.user != null) { // たぶんnullになることは無いはずだけど一応
 			for (const antenna of myAntennas) {
-				if (checkHitAntenna(antenna, note, note.user as any, undefined, Array.from(following))) {
+				if (await checkHitAntenna(antenna, note, note.user as any, undefined, Array.from(following))) {
 					readAntennaNotes.push(note);
 				}
 			}

From 02f6142d018dbce0b56dec15fd18d04cbce80365 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 22:52:16 +0900
Subject: [PATCH 70/91] app, messaging-message

---
 src/models/repositories/app.ts               | 6 ++----
 src/models/repositories/messaging-message.ts | 6 ++----
 src/services/push-notification.ts            | 4 ++--
 3 files changed, 6 insertions(+), 10 deletions(-)

diff --git a/src/models/repositories/app.ts b/src/models/repositories/app.ts
index 2287bd4390..3bd7af05a0 100644
--- a/src/models/repositories/app.ts
+++ b/src/models/repositories/app.ts
@@ -1,9 +1,7 @@
 import { EntityRepository, Repository } from 'typeorm';
 import { App } from '@/models/entities/app';
 import { AccessTokens } from '../index';
-import { SchemaType } from '@/misc/schema';
-
-export type PackedApp = SchemaType<typeof packedAppSchema>;
+import { Packed } from '@/misc/schema';
 
 @EntityRepository(App)
 export class AppRepository extends Repository<App> {
@@ -15,7 +13,7 @@ export class AppRepository extends Repository<App> {
 			includeSecret?: boolean,
 			includeProfileImageIds?: boolean
 		}
-	): Promise<PackedApp> {
+	): Promise<Packed<'App'>> {
 		const opts = Object.assign({
 			detail: false,
 			includeSecret: false,
diff --git a/src/models/repositories/messaging-message.ts b/src/models/repositories/messaging-message.ts
index f97905af2f..abdff63689 100644
--- a/src/models/repositories/messaging-message.ts
+++ b/src/models/repositories/messaging-message.ts
@@ -1,11 +1,9 @@
 import { EntityRepository, Repository } from 'typeorm';
 import { MessagingMessage } from '@/models/entities/messaging-message';
 import { Users, DriveFiles, UserGroups } from '../index';
-import { SchemaType } from '@/misc/schema';
+import { Packed } from '@/misc/schema';
 import { User } from '@/models/entities/user';
 
-export type PackedMessagingMessage = SchemaType<typeof packedMessagingMessageSchema>;
-
 @EntityRepository(MessagingMessage)
 export class MessagingMessageRepository extends Repository<MessagingMessage> {
 	public validateText(text: string): boolean {
@@ -19,7 +17,7 @@ export class MessagingMessageRepository extends Repository<MessagingMessage> {
 			populateRecipient?: boolean,
 			populateGroup?: boolean,
 		}
-	): Promise<PackedMessagingMessage> {
+	): Promise<Packed<'MessagingMessage'>> {
 		const opts = options || {
 			populateRecipient: true,
 			populateGroup: true,
diff --git a/src/services/push-notification.ts b/src/services/push-notification.ts
index 5bd7499692..23781acbe2 100644
--- a/src/services/push-notification.ts
+++ b/src/services/push-notification.ts
@@ -3,10 +3,10 @@ import config from '@/config/index';
 import { SwSubscriptions } from '@/models/index';
 import { fetchMeta } from '@/misc/fetch-meta';
 import { PackedNotification } from '../models/repositories/notification';
-import { PackedMessagingMessage } from '../models/repositories/messaging-message';
+import { Packed } from '@/misc/schema';
 
 type notificationType = 'notification' | 'unreadMessagingMessage';
-type notificationBody = PackedNotification | PackedMessagingMessage;
+type notificationBody = PackedNotification | Packed<'MessagingMessage'>;
 
 export default async function(userId: string, type: notificationType, body: notificationBody) {
 	const meta = await fetchMeta();

From 295ad3521091617bfdc1146127bcfc5ad4aba275 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 22:53:06 +0900
Subject: [PATCH 71/91] notification

---
 src/models/repositories/notification.ts | 6 ++----
 src/services/push-notification.ts       | 3 +--
 2 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/src/models/repositories/notification.ts b/src/models/repositories/notification.ts
index b7f9e3643c..d1cf9b087e 100644
--- a/src/models/repositories/notification.ts
+++ b/src/models/repositories/notification.ts
@@ -2,15 +2,13 @@ import { EntityRepository, In, Repository } from 'typeorm';
 import { Users, Notes, UserGroupInvitations, AccessTokens, NoteReactions } from '../index';
 import { Notification } from '@/models/entities/notification';
 import { awaitAll } from '@/prelude/await-all';
-import { SchemaType } from '@/misc/schema';
+import { Packed } from '@/misc/schema';
 import { Note } from '@/models/entities/note';
 import { NoteReaction } from '@/models/entities/note-reaction';
 import { User } from '@/models/entities/user';
 import { aggregateNoteEmojis, prefetchEmojis } from '@/misc/populate-emojis';
 import { notificationTypes } from '@/types';
 
-export type PackedNotification = SchemaType<typeof packedNotificationSchema>;
-
 @EntityRepository(Notification)
 export class NotificationRepository extends Repository<Notification> {
 	public async pack(
@@ -20,7 +18,7 @@ export class NotificationRepository extends Repository<Notification> {
 				myReactions: Map<Note['id'], NoteReaction | null>;
 			};
 		}
-	): Promise<PackedNotification> {
+	): Promise<Packed<'Notification'>> {
 		const notification = typeof src === 'object' ? src : await this.findOneOrFail(src);
 		const token = notification.appAccessTokenId ? await AccessTokens.findOneOrFail(notification.appAccessTokenId) : null;
 
diff --git a/src/services/push-notification.ts b/src/services/push-notification.ts
index 23781acbe2..5949d11b3b 100644
--- a/src/services/push-notification.ts
+++ b/src/services/push-notification.ts
@@ -2,11 +2,10 @@ import * as push from 'web-push';
 import config from '@/config/index';
 import { SwSubscriptions } from '@/models/index';
 import { fetchMeta } from '@/misc/fetch-meta';
-import { PackedNotification } from '../models/repositories/notification';
 import { Packed } from '@/misc/schema';
 
 type notificationType = 'notification' | 'unreadMessagingMessage';
-type notificationBody = PackedNotification | Packed<'MessagingMessage'>;
+type notificationBody = Packed<'Notification'> | Packed<'MessagingMessage'>;
 
 export default async function(userId: string, type: notificationType, body: notificationBody) {
 	const meta = await fetchMeta();

From 59ac258272a02e27be8b5fb7a13d4b2b1e1ba8ea Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 22:54:15 +0900
Subject: [PATCH 72/91] drive-file

---
 src/models/repositories/drive-file.ts | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/src/models/repositories/drive-file.ts b/src/models/repositories/drive-file.ts
index 63bd020cbe..ddf9a46afd 100644
--- a/src/models/repositories/drive-file.ts
+++ b/src/models/repositories/drive-file.ts
@@ -4,14 +4,12 @@ import { Users, DriveFolders } from '../index';
 import { User } from '@/models/entities/user';
 import { toPuny } from '@/misc/convert-host';
 import { awaitAll } from '@/prelude/await-all';
-import { SchemaType } from '@/misc/schema';
+import { Packed } from '@/misc/schema';
 import config from '@/config/index';
 import { query, appendQuery } from '@/prelude/url';
 import { Meta } from '@/models/entities/meta';
 import { fetchMeta } from '@/misc/fetch-meta';
 
-export type PackedDriveFile = SchemaType<typeof packedDriveFileSchema>;
-
 type PackOptions = {
 	detail?: boolean,
 	self?: boolean,
@@ -99,12 +97,12 @@ export class DriveFileRepository extends Repository<DriveFile> {
 		return parseInt(sum, 10) || 0;
 	}
 
-	public async pack(src: DriveFile['id'], options?: PackOptions): Promise<PackedDriveFile | null>;
-	public async pack(src: DriveFile, options?: PackOptions): Promise<PackedDriveFile>;
+	public async pack(src: DriveFile['id'], options?: PackOptions): Promise<Packed<'DriveFile'> | null>;
+	public async pack(src: DriveFile, options?: PackOptions): Promise<Packed<'DriveFile'>>;
 	public async pack(
 		src: DriveFile['id'] | DriveFile,
 		options?: PackOptions
-	): Promise<PackedDriveFile | null> {
+	): Promise<Packed<'DriveFile'> | null> {
 		const opts = Object.assign({
 			detail: false,
 			self: false

From 8f67adb8174255617666264fd931e7a7d9592cf4 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 22:54:51 +0900
Subject: [PATCH 73/91] drive-folder

---
 src/models/repositories/drive-folder.ts | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/models/repositories/drive-folder.ts b/src/models/repositories/drive-folder.ts
index bc73018f29..8ef6f01b5d 100644
--- a/src/models/repositories/drive-folder.ts
+++ b/src/models/repositories/drive-folder.ts
@@ -2,9 +2,7 @@ import { EntityRepository, Repository } from 'typeorm';
 import { DriveFolders, DriveFiles } from '../index';
 import { DriveFolder } from '@/models/entities/drive-folder';
 import { awaitAll } from '@/prelude/await-all';
-import { SchemaType } from '@/misc/schema';
-
-export type PackedDriveFolder = SchemaType<typeof packedDriveFolderSchema>;
+import { Packed } from '@/misc/schema';
 
 @EntityRepository(DriveFolder)
 export class DriveFolderRepository extends Repository<DriveFolder> {
@@ -20,7 +18,7 @@ export class DriveFolderRepository extends Repository<DriveFolder> {
 		options?: {
 			detail: boolean
 		}
-	): Promise<PackedDriveFolder> {
+	): Promise<Packed<'DriveFolder'>> {
 		const opts = Object.assign({
 			detail: false
 		}, options);

From fad785bebf966cdb560f4d482bf052fbc59981ec Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 22:55:21 +0900
Subject: [PATCH 74/91] following

---
 src/models/repositories/following.ts | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/models/repositories/following.ts b/src/models/repositories/following.ts
index 24ddd0d676..b1f716069f 100644
--- a/src/models/repositories/following.ts
+++ b/src/models/repositories/following.ts
@@ -2,7 +2,7 @@ import { EntityRepository, Repository } from 'typeorm';
 import { Users } from '../index';
 import { Following } from '@/models/entities/following';
 import { awaitAll } from '@/prelude/await-all';
-import { SchemaType } from '@/misc/schema';
+import { Packed } from '@/misc/schema';
 import { User } from '@/models/entities/user';
 
 type LocalFollowerFollowing = Following & {
@@ -29,8 +29,6 @@ type RemoteFolloweeFollowing = Following & {
 	followeeSharedInbox: string;
 };
 
-export type PackedFollowing = SchemaType<typeof packedFollowingSchema>;
-
 @EntityRepository(Following)
 export class FollowingRepository extends Repository<Following> {
 	public isLocalFollower(following: Following): following is LocalFollowerFollowing {
@@ -56,7 +54,7 @@ export class FollowingRepository extends Repository<Following> {
 			populateFollowee?: boolean;
 			populateFollower?: boolean;
 		}
-	): Promise<PackedFollowing> {
+	): Promise<Packed<'Following'>> {
 		const following = typeof src === 'object' ? src : await this.findOneOrFail(src);
 
 		if (opts == null) opts = {};

From b98687244d67d595a73b374518a2264e37e71ff9 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 22:56:03 +0900
Subject: [PATCH 75/91] muting

---
 src/models/repositories/muting.ts | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/models/repositories/muting.ts b/src/models/repositories/muting.ts
index d957b1792d..869afd3c4e 100644
--- a/src/models/repositories/muting.ts
+++ b/src/models/repositories/muting.ts
@@ -2,17 +2,15 @@ import { EntityRepository, Repository } from 'typeorm';
 import { Users } from '../index';
 import { Muting } from '@/models/entities/muting';
 import { awaitAll } from '@/prelude/await-all';
-import { SchemaType } from '@/misc/schema';
+import { Packed } from '@/misc/schema';
 import { User } from '@/models/entities/user';
 
-export type PackedMuting = SchemaType<typeof packedMutingSchema>;
-
 @EntityRepository(Muting)
 export class MutingRepository extends Repository<Muting> {
 	public async pack(
 		src: Muting['id'] | Muting,
 		me?: { id: User['id'] } | null | undefined
-	): Promise<PackedMuting> {
+	): Promise<Packed<'Muting'>> {
 		const muting = typeof src === 'object' ? src : await this.findOneOrFail(src);
 
 		return await awaitAll({

From 3d54fb24d5f776821ec2c65da6e13ead7296ee85 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 22:56:32 +0900
Subject: [PATCH 76/91] blocking

---
 src/models/repositories/blocking.ts | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/models/repositories/blocking.ts b/src/models/repositories/blocking.ts
index 515b3a6b16..ac60c9a4ce 100644
--- a/src/models/repositories/blocking.ts
+++ b/src/models/repositories/blocking.ts
@@ -2,17 +2,15 @@ import { EntityRepository, Repository } from 'typeorm';
 import { Users } from '../index';
 import { Blocking } from '@/models/entities/blocking';
 import { awaitAll } from '@/prelude/await-all';
-import { SchemaType } from '@/misc/schema';
+import { Packed } from '@/misc/schema';
 import { User } from '@/models/entities/user';
 
-export type PackedBlocking = SchemaType<typeof packedBlockingSchema>;
-
 @EntityRepository(Blocking)
 export class BlockingRepository extends Repository<Blocking> {
 	public async pack(
 		src: Blocking['id'] | Blocking,
 		me?: { id: User['id'] } | null | undefined
-	): Promise<PackedBlocking> {
+	): Promise<Packed<'Blocking'>> {
 		const blocking = typeof src === 'object' ? src : await this.findOneOrFail(src);
 
 		return await awaitAll({

From 4dfebbd0fee3957c1f591703c158ed3860554556 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 22:56:55 +0900
Subject: [PATCH 77/91] hashtag

---
 src/models/repositories/hashtag.ts | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/models/repositories/hashtag.ts b/src/models/repositories/hashtag.ts
index ee42ad16b6..d52f6ba7c6 100644
--- a/src/models/repositories/hashtag.ts
+++ b/src/models/repositories/hashtag.ts
@@ -1,14 +1,12 @@
 import { EntityRepository, Repository } from 'typeorm';
 import { Hashtag } from '@/models/entities/hashtag';
-import { SchemaType } from '@/misc/schema';
-
-export type PackedHashtag = SchemaType<typeof packedHashtagSchema>;
+import { Packed } from '@/misc/schema';
 
 @EntityRepository(Hashtag)
 export class HashtagRepository extends Repository<Hashtag> {
 	public async pack(
 		src: Hashtag,
-	): Promise<PackedHashtag> {
+	): Promise<Packed<'Hashtag'>> {
 		return {
 			tag: src.name,
 			mentionedUsersCount: src.mentionedUsersCount,

From 2c7daf8f0ee4d1807abbd4eecc1e29ed4bb99ae7 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 22:57:23 +0900
Subject: [PATCH 78/91] page

---
 src/models/repositories/page.ts | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/models/repositories/page.ts b/src/models/repositories/page.ts
index 1a61e2c99c..3a3642d7ec 100644
--- a/src/models/repositories/page.ts
+++ b/src/models/repositories/page.ts
@@ -1,19 +1,17 @@
 import { EntityRepository, Repository } from 'typeorm';
 import { Page } from '@/models/entities/page';
-import { SchemaType } from '@/misc/schema';
+import { Packed } from '@/misc/schema';
 import { Users, DriveFiles, PageLikes } from '../index';
 import { awaitAll } from '@/prelude/await-all';
 import { DriveFile } from '@/models/entities/drive-file';
 import { User } from '@/models/entities/user';
 
-export type PackedPage = SchemaType<typeof packedPageSchema>;
-
 @EntityRepository(Page)
 export class PageRepository extends Repository<Page> {
 	public async pack(
 		src: Page['id'] | Page,
 		me?: { id: User['id'] } | null | undefined,
-	): Promise<PackedPage> {
+	): Promise<Packed<'Page'>> {
 		const meId = me ? me.id : null;
 		const page = typeof src === 'object' ? src : await this.findOneOrFail(src);
 

From 8e9cfb8a90a88de90b3396d83f2fb7cfb1f2d60f Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 23:03:58 +0900
Subject: [PATCH 79/91] app (with modifying schema)

---
 src/models/repositories/app.ts | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/src/models/repositories/app.ts b/src/models/repositories/app.ts
index 3bd7af05a0..c48d069558 100644
--- a/src/models/repositories/app.ts
+++ b/src/models/repositories/app.ts
@@ -50,13 +50,9 @@ export const packedAppSchema = {
 			type: 'string' as const,
 			optional: false as const, nullable: false as const
 		},
-		createdAt: {
+		callbackUrl: {
 			type: 'string' as const,
-			optional: false as const, nullable: false as const
-		},
-		lastUsedAt: {
-			type: 'string' as const,
-			optional: false as const, nullable: false as const
+			optional: false as const, nullable: true as const
 		},
 		permission: {
 			type: 'array' as const,

From 5ed72999a9520e58e66823ba156a2bb54be2a7f6 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 23:04:48 +0900
Subject: [PATCH 80/91] import user?

---
 src/models/repositories/app.ts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/models/repositories/app.ts b/src/models/repositories/app.ts
index c48d069558..0226edad11 100644
--- a/src/models/repositories/app.ts
+++ b/src/models/repositories/app.ts
@@ -2,6 +2,7 @@ import { EntityRepository, Repository } from 'typeorm';
 import { App } from '@/models/entities/app';
 import { AccessTokens } from '../index';
 import { Packed } from '@/misc/schema';
+import { User } from '../entities/user';
 
 @EntityRepository(App)
 export class AppRepository extends Repository<App> {

From df10aa40f4b2ff388423ad455f0c76dc598e6c07 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 23:05:28 +0900
Subject: [PATCH 81/91] channel

---
 src/models/repositories/channel.ts | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/models/repositories/channel.ts b/src/models/repositories/channel.ts
index 4bb829f570..5c7d095473 100644
--- a/src/models/repositories/channel.ts
+++ b/src/models/repositories/channel.ts
@@ -1,17 +1,15 @@
 import { EntityRepository, Repository } from 'typeorm';
 import { Channel } from '@/models/entities/channel';
-import { SchemaType } from '@/misc/schema';
+import { Packed } from '@/misc/schema';
 import { DriveFiles, ChannelFollowings, NoteUnreads } from '../index';
 import { User } from '@/models/entities/user';
 
-export type PackedChannel = SchemaType<typeof packedChannelSchema>;
-
 @EntityRepository(Channel)
 export class ChannelRepository extends Repository<Channel> {
 	public async pack(
 		src: Channel['id'] | Channel,
 		me?: { id: User['id'] } | null | undefined,
-	): Promise<PackedChannel> {
+	): Promise<Packed<'Channel'>> {
 		const channel = typeof src === 'object' ? src : await this.findOneOrFail(src);
 		const meId = me ? me.id : null;
 

From 3bf7e4bf02993aedcb31af4c4ccabc3d04ea45a8 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 23:06:04 +0900
Subject: [PATCH 82/91] antenna

---
 src/models/repositories/antenna.ts | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/models/repositories/antenna.ts b/src/models/repositories/antenna.ts
index e61eed5e08..657de55581 100644
--- a/src/models/repositories/antenna.ts
+++ b/src/models/repositories/antenna.ts
@@ -1,15 +1,13 @@
 import { EntityRepository, Repository } from 'typeorm';
 import { Antenna } from '@/models/entities/antenna';
-import { SchemaType } from '@/misc/schema';
+import { Packed } from '@/misc/schema';
 import { AntennaNotes, UserGroupJoinings } from '../index';
 
-export type PackedAntenna = SchemaType<typeof packedAntennaSchema>;
-
 @EntityRepository(Antenna)
 export class AntennaRepository extends Repository<Antenna> {
 	public async pack(
 		src: Antenna['id'] | Antenna,
-	): Promise<PackedAntenna> {
+	): Promise<Packed<'Antenna'>> {
 		const antenna = typeof src === 'object' ? src : await this.findOneOrFail(src);
 
 		const hasUnreadNote = (await AntennaNotes.findOne({ antennaId: antenna.id, read: false })) != null;

From 3a4e3d78b2a7af8be8c47e1d991eddac5c8afa60 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 23:06:27 +0900
Subject: [PATCH 83/91] clip

---
 src/models/repositories/clip.ts | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/models/repositories/clip.ts b/src/models/repositories/clip.ts
index e3d718bef4..7892811d48 100644
--- a/src/models/repositories/clip.ts
+++ b/src/models/repositories/clip.ts
@@ -1,16 +1,14 @@
 import { EntityRepository, Repository } from 'typeorm';
 import { Clip } from '@/models/entities/clip';
-import { SchemaType } from '@/misc/schema';
+import { Packed } from '@/misc/schema';
 import { Users } from '../index';
 import { awaitAll } from '@/prelude/await-all';
 
-export type PackedClip = SchemaType<typeof packedClipSchema>;
-
 @EntityRepository(Clip)
 export class ClipRepository extends Repository<Clip> {
 	public async pack(
 		src: Clip['id'] | Clip,
-	): Promise<PackedClip> {
+	): Promise<Packed<'Clip'>> {
 		const clip = typeof src === 'object' ? src : await this.findOneOrFail(src);
 
 		return await awaitAll({

From c71844c2921443120a6a9e62ac4feff157f101a9 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 23:07:45 +0900
Subject: [PATCH 84/91] gallery-post

---
 src/models/repositories/gallery-post.ts | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/models/repositories/gallery-post.ts b/src/models/repositories/gallery-post.ts
index afa22e9edf..4f666ff252 100644
--- a/src/models/repositories/gallery-post.ts
+++ b/src/models/repositories/gallery-post.ts
@@ -1,18 +1,16 @@
 import { EntityRepository, Repository } from 'typeorm';
 import { GalleryPost } from '@/models/entities/gallery-post';
-import { SchemaType } from '@/misc/schema';
+import { Packed } from '@/misc/schema';
 import { Users, DriveFiles, GalleryLikes } from '../index';
 import { awaitAll } from '@/prelude/await-all';
 import { User } from '@/models/entities/user';
 
-export type PackedGalleryPost = SchemaType<typeof packedGalleryPostSchema>;
-
 @EntityRepository(GalleryPost)
 export class GalleryPostRepository extends Repository<GalleryPost> {
 	public async pack(
 		src: GalleryPost['id'] | GalleryPost,
 		me?: { id: User['id'] } | null | undefined,
-	): Promise<PackedGalleryPost> {
+	): Promise<Packed<'GalleryPost'>> {
 		const meId = me ? me.id : null;
 		const post = typeof src === 'object' ? src : await this.findOneOrFail(src);
 

From 77ed1d7dc4f891c8356a61d6647d524c02e25dfd Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 23:08:10 +0900
Subject: [PATCH 85/91] emoji

---
 src/models/repositories/emoji.ts | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/models/repositories/emoji.ts b/src/models/repositories/emoji.ts
index 24f03574fe..7985c27aba 100644
--- a/src/models/repositories/emoji.ts
+++ b/src/models/repositories/emoji.ts
@@ -1,14 +1,12 @@
 import { EntityRepository, Repository } from 'typeorm';
 import { Emoji } from '@/models/entities/emoji';
-import { SchemaType } from '@/misc/schema';
-
-export type PackedEmoji = SchemaType<typeof packedEmojiSchema>;
+import { Packed } from '@/misc/schema';
 
 @EntityRepository(Emoji)
 export class EmojiRepository extends Repository<Emoji> {
 	public async pack(
 		src: Emoji['id'] | Emoji,
-	): Promise<PackedEmoji> {
+	): Promise<Packed<'Emoji'>> {
 		const emoji = typeof src === 'object' ? src : await this.findOneOrFail(src);
 
 		return {

From 6b72d888f82063f2f0d56170b36f5ffa94f6e6a1 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 23:08:42 +0900
Subject: [PATCH 86/91] Packed

---
 src/models/repositories/games/reversi/game.ts | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/models/repositories/games/reversi/game.ts b/src/models/repositories/games/reversi/game.ts
index 121a3cad74..9adb386fa9 100644
--- a/src/models/repositories/games/reversi/game.ts
+++ b/src/models/repositories/games/reversi/game.ts
@@ -2,9 +2,7 @@ import { User } from '@/models/entities/user';
 import { EntityRepository, Repository } from 'typeorm';
 import { Users } from '../../../index';
 import { ReversiGame } from '@/models/entities/games/reversi/game';
-import { SchemaType } from '@/misc/schema';
-
-export type PackedReversiGame = SchemaType<typeof packedReversiGameSchema>;
+import { Packed } from '@/misc/schema';
 
 @EntityRepository(ReversiGame)
 export class ReversiGameRepository extends Repository<ReversiGame> {
@@ -14,7 +12,7 @@ export class ReversiGameRepository extends Repository<ReversiGame> {
 		options?: {
 			detail?: boolean
 		}
-	): Promise<PackedReversiGame> {
+	): Promise<Packed<'ReversiGame'>> {
 		const opts = Object.assign({
 			detail: true
 		}, options);

From f8df218bbbdbbac76b548a362cebbb39b5c5cd3d Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 23:09:17 +0900
Subject: [PATCH 87/91] reversi-matching

---
 src/models/repositories/games/reversi/matching.ts | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/models/repositories/games/reversi/matching.ts b/src/models/repositories/games/reversi/matching.ts
index 0cf3b9fd35..b4515800df 100644
--- a/src/models/repositories/games/reversi/matching.ts
+++ b/src/models/repositories/games/reversi/matching.ts
@@ -3,16 +3,14 @@ import { ReversiMatching } from '@/models/entities/games/reversi/matching';
 import { Users } from '../../../index';
 import { awaitAll } from '@/prelude/await-all';
 import { User } from '@/models/entities/user';
-import { SchemaType } from '@/misc/schema';
-
-export type PackedReversiMatching = SchemaType<typeof packedReversiMatchingSchema>;
+import { Packed } from '@/misc/schema';
 
 @EntityRepository(ReversiMatching)
 export class ReversiMatchingRepository extends Repository<ReversiMatching> {
 	public async pack(
 		src: ReversiMatching['id'] | ReversiMatching,
 		me: { id: User['id'] }
-	): Promise<PackedReversiMatching> {
+	): Promise<Packed<'ReversiMatching'>> {
 		const matching = typeof src === 'object' ? src : await this.findOneOrFail(src);
 
 		return await awaitAll({

From b2f27e9b4ec7bcac8bdd6e8c2a5b89811f4745b4 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 23:14:50 +0900
Subject: [PATCH 88/91] update stream.ts

---
 src/services/stream.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/services/stream.ts b/src/services/stream.ts
index c21ccb4a74..2c308a1b54 100644
--- a/src/services/stream.ts
+++ b/src/services/stream.ts
@@ -25,7 +25,7 @@ import {
 	UserListStreamTypes,
 	UserStreamTypes
 } from '@/server/api/stream/types';
-import { PackedNote } from '@/models/repositories/note';
+import { Packed } from '@/misc/schema';
 
 class Publisher {
 	private publish = (channel: StreamChannels, type: string | null, value?: any): void => {
@@ -98,7 +98,7 @@ class Publisher {
 		this.publish(`reversiGameStream:${gameId}`, type, typeof value === 'undefined' ? null : value);
 	}
 
-	public publishNotesStream = (note: PackedNote): void => {
+	public publishNotesStream = (note: Packed<'Note'>): void => {
 		this.publish('notesStream', null, note);
 	}
 

From 7fd5152f73a5850d8148e96087f2ecf2d1a863f6 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 23:21:19 +0900
Subject: [PATCH 89/91] 
 https://github.com/misskey-dev/misskey/pull/7769#issuecomment-917542339

---
 src/server/api/stream/types.ts | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/server/api/stream/types.ts b/src/server/api/stream/types.ts
index b3a2df0020..4508b4a371 100644
--- a/src/server/api/stream/types.ts
+++ b/src/server/api/stream/types.ts
@@ -19,9 +19,10 @@ import { Packed } from '@/misc/schema';
 
 // 辞書(interface or type)から{ type, body }ユニオンを定義
 // https://stackoverflow.com/questions/49311989/can-i-infer-the-type-of-a-value-using-extends-keyof-type
+type EventUnions<T extends object> = { [K in keyof T]: { type: K; body: T[K]; } }
 type EventUnionFromDictionary<
 	T extends object,
-	U = { [K in keyof T]: { type: K; body: T[K]; } }
+	U = EventUnions<T>
 > = U[keyof U];
 
 //#region Stream type-body definitions

From 9f4ad3c8730bd8350321f63a2a5b494ac7ee7af2 Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 23:38:12 +0900
Subject: [PATCH 90/91] fix lint

---
 src/server/api/stream/types.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/server/api/stream/types.ts b/src/server/api/stream/types.ts
index 4508b4a371..72451628de 100644
--- a/src/server/api/stream/types.ts
+++ b/src/server/api/stream/types.ts
@@ -19,7 +19,7 @@ import { Packed } from '@/misc/schema';
 
 // 辞書(interface or type)から{ type, body }ユニオンを定義
 // https://stackoverflow.com/questions/49311989/can-i-infer-the-type-of-a-value-using-extends-keyof-type
-type EventUnions<T extends object> = { [K in keyof T]: { type: K; body: T[K]; } }
+type EventUnions<T extends object> = { [K in keyof T]: { type: K; body: T[K]; } };
 type EventUnionFromDictionary<
 	T extends object,
 	U = EventUnions<T>

From 2a591861e7c9f55d716b9e90105bbce0b258ce7b Mon Sep 17 00:00:00 2001
From: tamaina <tamaina@hotmail.co.jp>
Date: Sun, 12 Sep 2021 23:49:21 +0900
Subject: [PATCH 91/91] clean up?

---
 src/server/api/stream/types.ts | 23 ++++++++++++-----------
 1 file changed, 12 insertions(+), 11 deletions(-)

diff --git a/src/server/api/stream/types.ts b/src/server/api/stream/types.ts
index 72451628de..c58a627eb3 100644
--- a/src/server/api/stream/types.ts
+++ b/src/server/api/stream/types.ts
@@ -17,14 +17,6 @@ import { Signin } from '@/models/entities/signin';
 import { Page } from '@/models/entities/page';
 import { Packed } from '@/misc/schema';
 
-// 辞書(interface or type)から{ type, body }ユニオンを定義
-// https://stackoverflow.com/questions/49311989/can-i-infer-the-type-of-a-value-using-extends-keyof-type
-type EventUnions<T extends object> = { [K in keyof T]: { type: K; body: T[K]; } };
-type EventUnionFromDictionary<
-	T extends object,
-	U = EventUnions<T>
-> = U[keyof U];
-
 //#region Stream type-body definitions
 export interface InternalStreamTypes {
 	antennaCreated: Antenna;
@@ -217,6 +209,15 @@ export interface AdminStreamTypes {
 }
 //#endregion
 
+// 辞書(interface or type)から{ type, body }ユニオンを定義
+// https://stackoverflow.com/questions/49311989/can-i-infer-the-type-of-a-value-using-extends-keyof-type
+// VS Codeの展開を防止するためにEvents型を定義
+type Events<T extends object> = { [K in keyof T]: { type: K; body: T[K]; } };
+type EventUnionFromDictionary<
+	T extends object,
+	U = Events<T>
+> = U[keyof U];
+
 // name/messages(spec) pairs dictionary
 export type StreamMessages = {
 	internal: {
@@ -287,11 +288,11 @@ export type StreamMessages = {
 
 // API event definitions
 // ストリームごとのEmitterの辞書を用意
-type EventsDictionary = { [x in keyof StreamMessages]: Emitter<EventEmitter, { [y in StreamMessages[x]['name']]: (e: StreamMessages[x]['spec']) => void }> };
+type EventEmitterDictionary = { [x in keyof StreamMessages]: Emitter<EventEmitter, { [y in StreamMessages[x]['name']]: (e: StreamMessages[x]['spec']) => void }> };
 // 共用体型を交差型にする型 https://stackoverflow.com/questions/54938141/typescript-convert-union-to-intersection
 type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
-// Emitter辞書をストリームごとに共用体型にし、UnionToIntersectionで交差型にする
-export type StreamEventEmitter = UnionToIntersection<EventsDictionary[keyof StreamMessages]>;
+// Emitter辞書から共用体型を作り、UnionToIntersectionで交差型にする
+export type StreamEventEmitter = UnionToIntersection<EventEmitterDictionary[keyof StreamMessages]>;
 // { [y in name]: (e: spec) => void }をまとめてその交差型をEmitterにかけるとts(2590)にひっかかる
 
 // provide stream channels union