perf(backend): avoid N+1 selects from user table when packing many entities (#13911)
				
					
				
			* perf(backend): avoid N+1 selects from `user` table when packing many entities * perf(backend): use `packMany` instead of mapping to `pack`
This commit is contained in:
		
							parent
							
								
									97be1a53ad
								
							
						
					
					
						commit
						514a65e453
					
				
					 24 changed files with 268 additions and 87 deletions
				
			
		| 
						 | 
					@ -10,6 +10,8 @@ import { awaitAll } from '@/misc/prelude/await-all.js';
 | 
				
			||||||
import type { MiAbuseUserReport } from '@/models/AbuseUserReport.js';
 | 
					import type { MiAbuseUserReport } from '@/models/AbuseUserReport.js';
 | 
				
			||||||
import { bindThis } from '@/decorators.js';
 | 
					import { bindThis } from '@/decorators.js';
 | 
				
			||||||
import { IdService } from '@/core/IdService.js';
 | 
					import { IdService } from '@/core/IdService.js';
 | 
				
			||||||
 | 
					import { isNotNull } from '@/misc/is-not-null.js';
 | 
				
			||||||
 | 
					import type { Packed } from '@/misc/json-schema.js';
 | 
				
			||||||
import { UserEntityService } from './UserEntityService.js';
 | 
					import { UserEntityService } from './UserEntityService.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable()
 | 
					@Injectable()
 | 
				
			||||||
| 
						 | 
					@ -26,6 +28,11 @@ export class AbuseUserReportEntityService {
 | 
				
			||||||
	@bindThis
 | 
						@bindThis
 | 
				
			||||||
	public async pack(
 | 
						public async pack(
 | 
				
			||||||
		src: MiAbuseUserReport['id'] | MiAbuseUserReport,
 | 
							src: MiAbuseUserReport['id'] | MiAbuseUserReport,
 | 
				
			||||||
 | 
							hint?: {
 | 
				
			||||||
 | 
								packedReporter?: Packed<'UserDetailedNotMe'>,
 | 
				
			||||||
 | 
								packedTargetUser?: Packed<'UserDetailedNotMe'>,
 | 
				
			||||||
 | 
								packedAssignee?: Packed<'UserDetailedNotMe'>,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		const report = typeof src === 'object' ? src : await this.abuseUserReportsRepository.findOneByOrFail({ id: src });
 | 
							const report = typeof src === 'object' ? src : await this.abuseUserReportsRepository.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,13 +44,13 @@ export class AbuseUserReportEntityService {
 | 
				
			||||||
			reporterId: report.reporterId,
 | 
								reporterId: report.reporterId,
 | 
				
			||||||
			targetUserId: report.targetUserId,
 | 
								targetUserId: report.targetUserId,
 | 
				
			||||||
			assigneeId: report.assigneeId,
 | 
								assigneeId: report.assigneeId,
 | 
				
			||||||
			reporter: this.userEntityService.pack(report.reporter ?? report.reporterId, null, {
 | 
								reporter: hint?.packedReporter ?? this.userEntityService.pack(report.reporter ?? report.reporterId, null, {
 | 
				
			||||||
				schema: 'UserDetailedNotMe',
 | 
									schema: 'UserDetailedNotMe',
 | 
				
			||||||
			}),
 | 
								}),
 | 
				
			||||||
			targetUser: this.userEntityService.pack(report.targetUser ?? report.targetUserId, null, {
 | 
								targetUser: hint?.packedTargetUser ?? this.userEntityService.pack(report.targetUser ?? report.targetUserId, null, {
 | 
				
			||||||
				schema: 'UserDetailedNotMe',
 | 
									schema: 'UserDetailedNotMe',
 | 
				
			||||||
			}),
 | 
								}),
 | 
				
			||||||
			assignee: report.assigneeId ? this.userEntityService.pack(report.assignee ?? report.assigneeId, null, {
 | 
								assignee: report.assigneeId ? hint?.packedAssignee ?? this.userEntityService.pack(report.assignee ?? report.assigneeId, null, {
 | 
				
			||||||
				schema: 'UserDetailedNotMe',
 | 
									schema: 'UserDetailedNotMe',
 | 
				
			||||||
			}) : null,
 | 
								}) : null,
 | 
				
			||||||
			forwarded: report.forwarded,
 | 
								forwarded: report.forwarded,
 | 
				
			||||||
| 
						 | 
					@ -51,9 +58,24 @@ export class AbuseUserReportEntityService {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@bindThis
 | 
						@bindThis
 | 
				
			||||||
	public packMany(
 | 
						public async packMany(
 | 
				
			||||||
		reports: any[],
 | 
							reports: MiAbuseUserReport[],
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(reports.map(x => this.pack(x)));
 | 
							const _reporters = reports.map(({ reporter, reporterId }) => reporter ?? reporterId);
 | 
				
			||||||
 | 
							const _targetUsers = reports.map(({ targetUser, targetUserId }) => targetUser ?? targetUserId);
 | 
				
			||||||
 | 
							const _assignees = reports.map(({ assignee, assigneeId }) => assignee ?? assigneeId).filter(isNotNull);
 | 
				
			||||||
 | 
							const _userMap = await this.userEntityService.packMany(
 | 
				
			||||||
 | 
								[..._reporters, ..._targetUsers, ..._assignees],
 | 
				
			||||||
 | 
								null,
 | 
				
			||||||
 | 
								{ schema: 'UserDetailedNotMe' },
 | 
				
			||||||
 | 
							).then(users => new Map(users.map(u => [u.id, u])));
 | 
				
			||||||
 | 
							return Promise.all(
 | 
				
			||||||
 | 
								reports.map(report => {
 | 
				
			||||||
 | 
									const packedReporter = _userMap.get(report.reporterId);
 | 
				
			||||||
 | 
									const packedTargetUser = _userMap.get(report.targetUserId);
 | 
				
			||||||
 | 
									const packedAssignee = report.assigneeId != null ? _userMap.get(report.assigneeId) : undefined;
 | 
				
			||||||
 | 
									return this.pack(report, { packedReporter, packedTargetUser, packedAssignee });
 | 
				
			||||||
 | 
								}),
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,6 +29,9 @@ export class BlockingEntityService {
 | 
				
			||||||
	public async pack(
 | 
						public async pack(
 | 
				
			||||||
		src: MiBlocking['id'] | MiBlocking,
 | 
							src: MiBlocking['id'] | MiBlocking,
 | 
				
			||||||
		me?: { id: MiUser['id'] } | null | undefined,
 | 
							me?: { id: MiUser['id'] } | null | undefined,
 | 
				
			||||||
 | 
							hint?: {
 | 
				
			||||||
 | 
								blockee?: Packed<'UserDetailedNotMe'>,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	): Promise<Packed<'Blocking'>> {
 | 
						): Promise<Packed<'Blocking'>> {
 | 
				
			||||||
		const blocking = typeof src === 'object' ? src : await this.blockingsRepository.findOneByOrFail({ id: src });
 | 
							const blocking = typeof src === 'object' ? src : await this.blockingsRepository.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,17 +39,20 @@ export class BlockingEntityService {
 | 
				
			||||||
			id: blocking.id,
 | 
								id: blocking.id,
 | 
				
			||||||
			createdAt: this.idService.parse(blocking.id).date.toISOString(),
 | 
								createdAt: this.idService.parse(blocking.id).date.toISOString(),
 | 
				
			||||||
			blockeeId: blocking.blockeeId,
 | 
								blockeeId: blocking.blockeeId,
 | 
				
			||||||
			blockee: this.userEntityService.pack(blocking.blockeeId, me, {
 | 
								blockee: hint?.blockee ?? this.userEntityService.pack(blocking.blockeeId, me, {
 | 
				
			||||||
				schema: 'UserDetailedNotMe',
 | 
									schema: 'UserDetailedNotMe',
 | 
				
			||||||
			}),
 | 
								}),
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@bindThis
 | 
						@bindThis
 | 
				
			||||||
	public packMany(
 | 
						public async packMany(
 | 
				
			||||||
		blockings: any[],
 | 
							blockings: MiBlocking[],
 | 
				
			||||||
		me: { id: MiUser['id'] },
 | 
							me: { id: MiUser['id'] },
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(blockings.map(x => this.pack(x, me)));
 | 
							const _blockees = blockings.map(({ blockee, blockeeId }) => blockee ?? blockeeId);
 | 
				
			||||||
 | 
							const _userMap = await this.userEntityService.packMany(_blockees, me, { schema: 'UserDetailedNotMe' })
 | 
				
			||||||
 | 
								.then(users => new Map(users.map(u => [u.id, u])));
 | 
				
			||||||
 | 
							return Promise.all(blockings.map(blocking => this.pack(blocking, me, { blockee: _userMap.get(blocking.blockeeId) })));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -35,6 +35,9 @@ export class ClipEntityService {
 | 
				
			||||||
	public async pack(
 | 
						public async pack(
 | 
				
			||||||
		src: MiClip['id'] | MiClip,
 | 
							src: MiClip['id'] | MiClip,
 | 
				
			||||||
		me?: { id: MiUser['id'] } | null | undefined,
 | 
							me?: { id: MiUser['id'] } | null | undefined,
 | 
				
			||||||
 | 
							hint?: {
 | 
				
			||||||
 | 
								packedUser?: Packed<'UserLite'>
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	): Promise<Packed<'Clip'>> {
 | 
						): Promise<Packed<'Clip'>> {
 | 
				
			||||||
		const meId = me ? me.id : null;
 | 
							const meId = me ? me.id : null;
 | 
				
			||||||
		const clip = typeof src === 'object' ? src : await this.clipsRepository.findOneByOrFail({ id: src });
 | 
							const clip = typeof src === 'object' ? src : await this.clipsRepository.findOneByOrFail({ id: src });
 | 
				
			||||||
| 
						 | 
					@ -44,7 +47,7 @@ export class ClipEntityService {
 | 
				
			||||||
			createdAt: this.idService.parse(clip.id).date.toISOString(),
 | 
								createdAt: this.idService.parse(clip.id).date.toISOString(),
 | 
				
			||||||
			lastClippedAt: clip.lastClippedAt ? clip.lastClippedAt.toISOString() : null,
 | 
								lastClippedAt: clip.lastClippedAt ? clip.lastClippedAt.toISOString() : null,
 | 
				
			||||||
			userId: clip.userId,
 | 
								userId: clip.userId,
 | 
				
			||||||
			user: this.userEntityService.pack(clip.user ?? clip.userId),
 | 
								user: hint?.packedUser ?? this.userEntityService.pack(clip.user ?? clip.userId),
 | 
				
			||||||
			name: clip.name,
 | 
								name: clip.name,
 | 
				
			||||||
			description: clip.description,
 | 
								description: clip.description,
 | 
				
			||||||
			isPublic: clip.isPublic,
 | 
								isPublic: clip.isPublic,
 | 
				
			||||||
| 
						 | 
					@ -55,11 +58,14 @@ export class ClipEntityService {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@bindThis
 | 
						@bindThis
 | 
				
			||||||
	public packMany(
 | 
						public async packMany(
 | 
				
			||||||
		clips: MiClip[],
 | 
							clips: MiClip[],
 | 
				
			||||||
		me?: { id: MiUser['id'] } | null | undefined,
 | 
							me?: { id: MiUser['id'] } | null | undefined,
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(clips.map(x => this.pack(x, me)));
 | 
							const _users = clips.map(({ user, userId }) => user ?? userId);
 | 
				
			||||||
 | 
							const _userMap = await this.userEntityService.packMany(_users, me)
 | 
				
			||||||
 | 
								.then(users => new Map(users.map(u => [u.id, u])));
 | 
				
			||||||
 | 
							return Promise.all(clips.map(clip => this.pack(clip, me, { packedUser: _userMap.get(clip.userId) })));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -222,6 +222,9 @@ export class DriveFileEntityService {
 | 
				
			||||||
	public async packNullable(
 | 
						public async packNullable(
 | 
				
			||||||
		src: MiDriveFile['id'] | MiDriveFile,
 | 
							src: MiDriveFile['id'] | MiDriveFile,
 | 
				
			||||||
		options?: PackOptions,
 | 
							options?: PackOptions,
 | 
				
			||||||
 | 
							hint?: {
 | 
				
			||||||
 | 
								packedUser?: Packed<'UserLite'>
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	): Promise<Packed<'DriveFile'> | null> {
 | 
						): Promise<Packed<'DriveFile'> | null> {
 | 
				
			||||||
		const opts = Object.assign({
 | 
							const opts = Object.assign({
 | 
				
			||||||
			detail: false,
 | 
								detail: false,
 | 
				
			||||||
| 
						 | 
					@ -249,7 +252,7 @@ export class DriveFileEntityService {
 | 
				
			||||||
				detail: true,
 | 
									detail: true,
 | 
				
			||||||
			}) : null,
 | 
								}) : null,
 | 
				
			||||||
			userId: file.userId,
 | 
								userId: file.userId,
 | 
				
			||||||
			user: (opts.withUser && file.userId) ? this.userEntityService.pack(file.userId) : null,
 | 
								user: (opts.withUser && file.userId) ? hint?.packedUser ?? this.userEntityService.pack(file.userId) : null,
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -258,7 +261,10 @@ export class DriveFileEntityService {
 | 
				
			||||||
		files: MiDriveFile[],
 | 
							files: MiDriveFile[],
 | 
				
			||||||
		options?: PackOptions,
 | 
							options?: PackOptions,
 | 
				
			||||||
	): Promise<Packed<'DriveFile'>[]> {
 | 
						): Promise<Packed<'DriveFile'>[]> {
 | 
				
			||||||
		const items = await Promise.all(files.map(f => this.packNullable(f, options)));
 | 
							const _user = files.map(({ user, userId }) => user ?? userId).filter(isNotNull);
 | 
				
			||||||
 | 
							const _userMap = await this.userEntityService.packMany(_user)
 | 
				
			||||||
 | 
								.then(users => new Map(users.map(user => [user.id, user])));
 | 
				
			||||||
 | 
							const items = await Promise.all(files.map(f => this.packNullable(f, options, f.userId ? { packedUser: _userMap.get(f.userId) } : {})));
 | 
				
			||||||
		return items.filter(isNotNull);
 | 
							return items.filter(isNotNull);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,6 +33,9 @@ export class FlashEntityService {
 | 
				
			||||||
	public async pack(
 | 
						public async pack(
 | 
				
			||||||
		src: MiFlash['id'] | MiFlash,
 | 
							src: MiFlash['id'] | MiFlash,
 | 
				
			||||||
		me?: { id: MiUser['id'] } | null | undefined,
 | 
							me?: { id: MiUser['id'] } | null | undefined,
 | 
				
			||||||
 | 
							hint?: {
 | 
				
			||||||
 | 
								packedUser?: Packed<'UserLite'>
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	): Promise<Packed<'Flash'>> {
 | 
						): Promise<Packed<'Flash'>> {
 | 
				
			||||||
		const meId = me ? me.id : null;
 | 
							const meId = me ? me.id : null;
 | 
				
			||||||
		const flash = typeof src === 'object' ? src : await this.flashsRepository.findOneByOrFail({ id: src });
 | 
							const flash = typeof src === 'object' ? src : await this.flashsRepository.findOneByOrFail({ id: src });
 | 
				
			||||||
| 
						 | 
					@ -42,7 +45,7 @@ export class FlashEntityService {
 | 
				
			||||||
			createdAt: this.idService.parse(flash.id).date.toISOString(),
 | 
								createdAt: this.idService.parse(flash.id).date.toISOString(),
 | 
				
			||||||
			updatedAt: flash.updatedAt.toISOString(),
 | 
								updatedAt: flash.updatedAt.toISOString(),
 | 
				
			||||||
			userId: flash.userId,
 | 
								userId: flash.userId,
 | 
				
			||||||
			user: this.userEntityService.pack(flash.user ?? flash.userId, me), // { schema: 'UserDetailed' } すると無限ループするので注意
 | 
								user: hint?.packedUser ?? this.userEntityService.pack(flash.user ?? flash.userId, me), // { schema: 'UserDetailed' } すると無限ループするので注意
 | 
				
			||||||
			title: flash.title,
 | 
								title: flash.title,
 | 
				
			||||||
			summary: flash.summary,
 | 
								summary: flash.summary,
 | 
				
			||||||
			script: flash.script,
 | 
								script: flash.script,
 | 
				
			||||||
| 
						 | 
					@ -52,11 +55,14 @@ export class FlashEntityService {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@bindThis
 | 
						@bindThis
 | 
				
			||||||
	public packMany(
 | 
						public async packMany(
 | 
				
			||||||
		flashs: MiFlash[],
 | 
							flashes: MiFlash[],
 | 
				
			||||||
		me?: { id: MiUser['id'] } | null | undefined,
 | 
							me?: { id: MiUser['id'] } | null | undefined,
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(flashs.map(x => this.pack(x, me)));
 | 
							const _users = flashes.map(({ user, userId }) => user ?? userId);
 | 
				
			||||||
 | 
							const _userMap = await this.userEntityService.packMany(_users, me)
 | 
				
			||||||
 | 
								.then(users => new Map(users.map(u => [u.id, u])));
 | 
				
			||||||
 | 
							return Promise.all(flashes.map(flash => this.pack(flash, me, { packedUser: _userMap.get(flash.userId) })));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,7 @@ import type { } from '@/models/Blocking.js';
 | 
				
			||||||
import type { MiUser } from '@/models/User.js';
 | 
					import type { MiUser } from '@/models/User.js';
 | 
				
			||||||
import type { MiFollowRequest } from '@/models/FollowRequest.js';
 | 
					import type { MiFollowRequest } from '@/models/FollowRequest.js';
 | 
				
			||||||
import { bindThis } from '@/decorators.js';
 | 
					import { bindThis } from '@/decorators.js';
 | 
				
			||||||
 | 
					import type { Packed } from '@/misc/json-schema.js';
 | 
				
			||||||
import { UserEntityService } from './UserEntityService.js';
 | 
					import { UserEntityService } from './UserEntityService.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable()
 | 
					@Injectable()
 | 
				
			||||||
| 
						 | 
					@ -26,14 +27,36 @@ export class FollowRequestEntityService {
 | 
				
			||||||
	public async pack(
 | 
						public async pack(
 | 
				
			||||||
		src: MiFollowRequest['id'] | MiFollowRequest,
 | 
							src: MiFollowRequest['id'] | MiFollowRequest,
 | 
				
			||||||
		me?: { id: MiUser['id'] } | null | undefined,
 | 
							me?: { id: MiUser['id'] } | null | undefined,
 | 
				
			||||||
 | 
							hint?: {
 | 
				
			||||||
 | 
								packedFollower?: Packed<'UserLite'>,
 | 
				
			||||||
 | 
								packedFollowee?: Packed<'UserLite'>,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		const request = typeof src === 'object' ? src : await this.followRequestsRepository.findOneByOrFail({ id: src });
 | 
							const request = typeof src === 'object' ? src : await this.followRequestsRepository.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			id: request.id,
 | 
								id: request.id,
 | 
				
			||||||
			follower: await this.userEntityService.pack(request.followerId, me),
 | 
								follower: hint?.packedFollower ?? await this.userEntityService.pack(request.followerId, me),
 | 
				
			||||||
			followee: await this.userEntityService.pack(request.followeeId, me),
 | 
								followee: hint?.packedFollowee ?? await this.userEntityService.pack(request.followeeId, me),
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@bindThis
 | 
				
			||||||
 | 
						public async packMany(
 | 
				
			||||||
 | 
							requests: MiFollowRequest[],
 | 
				
			||||||
 | 
							me?: { id: MiUser['id'] } | null | undefined,
 | 
				
			||||||
 | 
						) {
 | 
				
			||||||
 | 
							const _followers = requests.map(({ follower, followerId }) => follower ?? followerId);
 | 
				
			||||||
 | 
							const _followees = requests.map(({ followee, followeeId }) => followee ?? followeeId);
 | 
				
			||||||
 | 
							const _userMap = await this.userEntityService.packMany([..._followers, ..._followees], me)
 | 
				
			||||||
 | 
								.then(users => new Map(users.map(u => [u.id, u])));
 | 
				
			||||||
 | 
							return Promise.all(
 | 
				
			||||||
 | 
								requests.map(req => {
 | 
				
			||||||
 | 
									const packedFollower = _userMap.get(req.followerId);
 | 
				
			||||||
 | 
									const packedFollowee = _userMap.get(req.followeeId);
 | 
				
			||||||
 | 
									return this.pack(req, me, { packedFollower, packedFollowee });
 | 
				
			||||||
 | 
								}),
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -78,6 +78,10 @@ export class FollowingEntityService {
 | 
				
			||||||
			populateFollowee?: boolean;
 | 
								populateFollowee?: boolean;
 | 
				
			||||||
			populateFollower?: boolean;
 | 
								populateFollower?: boolean;
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							hint?: {
 | 
				
			||||||
 | 
								packedFollowee?: Packed<'UserDetailedNotMe'>,
 | 
				
			||||||
 | 
								packedFollower?: Packed<'UserDetailedNotMe'>,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	): Promise<Packed<'Following'>> {
 | 
						): Promise<Packed<'Following'>> {
 | 
				
			||||||
		const following = typeof src === 'object' ? src : await this.followingsRepository.findOneByOrFail({ id: src });
 | 
							const following = typeof src === 'object' ? src : await this.followingsRepository.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -88,25 +92,35 @@ export class FollowingEntityService {
 | 
				
			||||||
			createdAt: this.idService.parse(following.id).date.toISOString(),
 | 
								createdAt: this.idService.parse(following.id).date.toISOString(),
 | 
				
			||||||
			followeeId: following.followeeId,
 | 
								followeeId: following.followeeId,
 | 
				
			||||||
			followerId: following.followerId,
 | 
								followerId: following.followerId,
 | 
				
			||||||
			followee: opts.populateFollowee ? this.userEntityService.pack(following.followee ?? following.followeeId, me, {
 | 
								followee: opts.populateFollowee ? hint?.packedFollowee ?? this.userEntityService.pack(following.followee ?? following.followeeId, me, {
 | 
				
			||||||
				schema: 'UserDetailedNotMe',
 | 
									schema: 'UserDetailedNotMe',
 | 
				
			||||||
			}) : undefined,
 | 
								}) : undefined,
 | 
				
			||||||
			follower: opts.populateFollower ? this.userEntityService.pack(following.follower ?? following.followerId, me, {
 | 
								follower: opts.populateFollower ? hint?.packedFollower ?? this.userEntityService.pack(following.follower ?? following.followerId, me, {
 | 
				
			||||||
				schema: 'UserDetailedNotMe',
 | 
									schema: 'UserDetailedNotMe',
 | 
				
			||||||
			}) : undefined,
 | 
								}) : undefined,
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@bindThis
 | 
						@bindThis
 | 
				
			||||||
	public packMany(
 | 
						public async packMany(
 | 
				
			||||||
		followings: any[],
 | 
							followings: MiFollowing[],
 | 
				
			||||||
		me?: { id: MiUser['id'] } | null | undefined,
 | 
							me?: { id: MiUser['id'] } | null | undefined,
 | 
				
			||||||
		opts?: {
 | 
							opts?: {
 | 
				
			||||||
			populateFollowee?: boolean;
 | 
								populateFollowee?: boolean;
 | 
				
			||||||
			populateFollower?: boolean;
 | 
								populateFollower?: boolean;
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(followings.map(x => this.pack(x, me, opts)));
 | 
							const _followees = opts?.populateFollowee ? followings.map(({ followee, followeeId }) => followee ?? followeeId) : [];
 | 
				
			||||||
 | 
							const _followers = opts?.populateFollower ? followings.map(({ follower, followerId }) => follower ?? followerId) : [];
 | 
				
			||||||
 | 
							const _userMap = await this.userEntityService.packMany([..._followees, ..._followers], me, { schema: 'UserDetailedNotMe' })
 | 
				
			||||||
 | 
								.then(users => new Map(users.map(u => [u.id, u])));
 | 
				
			||||||
 | 
							return Promise.all(
 | 
				
			||||||
 | 
								followings.map(following => {
 | 
				
			||||||
 | 
									const packedFollowee = opts?.populateFollowee ? _userMap.get(following.followeeId) : undefined;
 | 
				
			||||||
 | 
									const packedFollower = opts?.populateFollower ? _userMap.get(following.followerId) : undefined;
 | 
				
			||||||
 | 
									return this.pack(following, me, opts, { packedFollowee, packedFollower });
 | 
				
			||||||
 | 
								}),
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -35,6 +35,9 @@ export class GalleryPostEntityService {
 | 
				
			||||||
	public async pack(
 | 
						public async pack(
 | 
				
			||||||
		src: MiGalleryPost['id'] | MiGalleryPost,
 | 
							src: MiGalleryPost['id'] | MiGalleryPost,
 | 
				
			||||||
		me?: { id: MiUser['id'] } | null | undefined,
 | 
							me?: { id: MiUser['id'] } | null | undefined,
 | 
				
			||||||
 | 
							hint?: {
 | 
				
			||||||
 | 
								packedUser?: Packed<'UserLite'>
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	): Promise<Packed<'GalleryPost'>> {
 | 
						): Promise<Packed<'GalleryPost'>> {
 | 
				
			||||||
		const meId = me ? me.id : null;
 | 
							const meId = me ? me.id : null;
 | 
				
			||||||
		const post = typeof src === 'object' ? src : await this.galleryPostsRepository.findOneByOrFail({ id: src });
 | 
							const post = typeof src === 'object' ? src : await this.galleryPostsRepository.findOneByOrFail({ id: src });
 | 
				
			||||||
| 
						 | 
					@ -44,7 +47,7 @@ export class GalleryPostEntityService {
 | 
				
			||||||
			createdAt: this.idService.parse(post.id).date.toISOString(),
 | 
								createdAt: this.idService.parse(post.id).date.toISOString(),
 | 
				
			||||||
			updatedAt: post.updatedAt.toISOString(),
 | 
								updatedAt: post.updatedAt.toISOString(),
 | 
				
			||||||
			userId: post.userId,
 | 
								userId: post.userId,
 | 
				
			||||||
			user: this.userEntityService.pack(post.user ?? post.userId, me),
 | 
								user: hint?.packedUser ?? this.userEntityService.pack(post.user ?? post.userId, me),
 | 
				
			||||||
			title: post.title,
 | 
								title: post.title,
 | 
				
			||||||
			description: post.description,
 | 
								description: post.description,
 | 
				
			||||||
			fileIds: post.fileIds,
 | 
								fileIds: post.fileIds,
 | 
				
			||||||
| 
						 | 
					@ -58,11 +61,14 @@ export class GalleryPostEntityService {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@bindThis
 | 
						@bindThis
 | 
				
			||||||
	public packMany(
 | 
						public async packMany(
 | 
				
			||||||
		posts: MiGalleryPost[],
 | 
							posts: MiGalleryPost[],
 | 
				
			||||||
		me?: { id: MiUser['id'] } | null | undefined,
 | 
							me?: { id: MiUser['id'] } | null | undefined,
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(posts.map(x => this.pack(x, me)));
 | 
							const _users = posts.map(({ user, userId }) => user ?? userId);
 | 
				
			||||||
 | 
							const _userMap = await this.userEntityService.packMany(_users, me)
 | 
				
			||||||
 | 
								.then(users => new Map(users.map(u => [u.id, u])));
 | 
				
			||||||
 | 
							return Promise.all(posts.map(post => this.pack(post, me, { packedUser: _userMap.get(post.userId) })));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,6 +12,7 @@ import type { MiUser } from '@/models/User.js';
 | 
				
			||||||
import type { MiRegistrationTicket } from '@/models/RegistrationTicket.js';
 | 
					import type { MiRegistrationTicket } from '@/models/RegistrationTicket.js';
 | 
				
			||||||
import { bindThis } from '@/decorators.js';
 | 
					import { bindThis } from '@/decorators.js';
 | 
				
			||||||
import { IdService } from '@/core/IdService.js';
 | 
					import { IdService } from '@/core/IdService.js';
 | 
				
			||||||
 | 
					import { isNotNull } from '@/misc/is-not-null.js';
 | 
				
			||||||
import { UserEntityService } from './UserEntityService.js';
 | 
					import { UserEntityService } from './UserEntityService.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable()
 | 
					@Injectable()
 | 
				
			||||||
| 
						 | 
					@ -29,6 +30,10 @@ export class InviteCodeEntityService {
 | 
				
			||||||
	public async pack(
 | 
						public async pack(
 | 
				
			||||||
		src: MiRegistrationTicket['id'] | MiRegistrationTicket,
 | 
							src: MiRegistrationTicket['id'] | MiRegistrationTicket,
 | 
				
			||||||
		me?: { id: MiUser['id'] } | null | undefined,
 | 
							me?: { id: MiUser['id'] } | null | undefined,
 | 
				
			||||||
 | 
							hints?: {
 | 
				
			||||||
 | 
								packedCreatedBy?: Packed<'UserLite'>,
 | 
				
			||||||
 | 
								packedUsedBy?: Packed<'UserLite'>,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	): Promise<Packed<'InviteCode'>> {
 | 
						): Promise<Packed<'InviteCode'>> {
 | 
				
			||||||
		const target = typeof src === 'object' ? src : await this.registrationTicketsRepository.findOneOrFail({
 | 
							const target = typeof src === 'object' ? src : await this.registrationTicketsRepository.findOneOrFail({
 | 
				
			||||||
			where: {
 | 
								where: {
 | 
				
			||||||
| 
						 | 
					@ -42,18 +47,28 @@ export class InviteCodeEntityService {
 | 
				
			||||||
			code: target.code,
 | 
								code: target.code,
 | 
				
			||||||
			expiresAt: target.expiresAt ? target.expiresAt.toISOString() : null,
 | 
								expiresAt: target.expiresAt ? target.expiresAt.toISOString() : null,
 | 
				
			||||||
			createdAt: this.idService.parse(target.id).date.toISOString(),
 | 
								createdAt: this.idService.parse(target.id).date.toISOString(),
 | 
				
			||||||
			createdBy: target.createdBy ? await this.userEntityService.pack(target.createdBy, me) : null,
 | 
								createdBy: target.createdBy ? hints?.packedCreatedBy ?? await this.userEntityService.pack(target.createdBy, me) : null,
 | 
				
			||||||
			usedBy: target.usedBy ? await this.userEntityService.pack(target.usedBy, me) : null,
 | 
								usedBy: target.usedBy ? hints?.packedUsedBy ?? await this.userEntityService.pack(target.usedBy, me) : null,
 | 
				
			||||||
			usedAt: target.usedAt ? target.usedAt.toISOString() : null,
 | 
								usedAt: target.usedAt ? target.usedAt.toISOString() : null,
 | 
				
			||||||
			used: !!target.usedAt,
 | 
								used: !!target.usedAt,
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@bindThis
 | 
						@bindThis
 | 
				
			||||||
	public packMany(
 | 
						public async packMany(
 | 
				
			||||||
		targets: any[],
 | 
							tickets: MiRegistrationTicket[],
 | 
				
			||||||
		me: { id: MiUser['id'] },
 | 
							me: { id: MiUser['id'] },
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(targets.map(x => this.pack(x, me)));
 | 
							const _createdBys = tickets.map(({ createdBy, createdById }) => createdBy ?? createdById).filter(isNotNull);
 | 
				
			||||||
 | 
							const _usedBys = tickets.map(({ usedBy, usedById }) => usedBy ?? usedById).filter(isNotNull);
 | 
				
			||||||
 | 
							const _userMap = await this.userEntityService.packMany([..._createdBys, ..._usedBys], me)
 | 
				
			||||||
 | 
								.then(users => new Map(users.map(u => [u.id, u])));
 | 
				
			||||||
 | 
							return Promise.all(
 | 
				
			||||||
 | 
								tickets.map(ticket => {
 | 
				
			||||||
 | 
									const packedCreatedBy = ticket.createdById != null ? _userMap.get(ticket.createdById) : undefined;
 | 
				
			||||||
 | 
									const packedUsedBy = ticket.usedById != null ? _userMap.get(ticket.usedById) : undefined;
 | 
				
			||||||
 | 
									return this.pack(ticket, me, { packedCreatedBy, packedUsedBy });
 | 
				
			||||||
 | 
								}),
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,9 +8,10 @@ import { DI } from '@/di-symbols.js';
 | 
				
			||||||
import type { ModerationLogsRepository } from '@/models/_.js';
 | 
					import type { ModerationLogsRepository } from '@/models/_.js';
 | 
				
			||||||
import { awaitAll } from '@/misc/prelude/await-all.js';
 | 
					import { awaitAll } from '@/misc/prelude/await-all.js';
 | 
				
			||||||
import type { } from '@/models/Blocking.js';
 | 
					import type { } from '@/models/Blocking.js';
 | 
				
			||||||
import type { MiModerationLog } from '@/models/ModerationLog.js';
 | 
					import { MiModerationLog } from '@/models/ModerationLog.js';
 | 
				
			||||||
import { bindThis } from '@/decorators.js';
 | 
					import { bindThis } from '@/decorators.js';
 | 
				
			||||||
import { IdService } from '@/core/IdService.js';
 | 
					import { IdService } from '@/core/IdService.js';
 | 
				
			||||||
 | 
					import type { Packed } from '@/misc/json-schema.js';
 | 
				
			||||||
import { UserEntityService } from './UserEntityService.js';
 | 
					import { UserEntityService } from './UserEntityService.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable()
 | 
					@Injectable()
 | 
				
			||||||
| 
						 | 
					@ -27,6 +28,9 @@ export class ModerationLogEntityService {
 | 
				
			||||||
	@bindThis
 | 
						@bindThis
 | 
				
			||||||
	public async pack(
 | 
						public async pack(
 | 
				
			||||||
		src: MiModerationLog['id'] | MiModerationLog,
 | 
							src: MiModerationLog['id'] | MiModerationLog,
 | 
				
			||||||
 | 
							hint?: {
 | 
				
			||||||
 | 
								packedUser?: Packed<'UserDetailedNotMe'>,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		const log = typeof src === 'object' ? src : await this.moderationLogsRepository.findOneByOrFail({ id: src });
 | 
							const log = typeof src === 'object' ? src : await this.moderationLogsRepository.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,17 +40,20 @@ export class ModerationLogEntityService {
 | 
				
			||||||
			type: log.type,
 | 
								type: log.type,
 | 
				
			||||||
			info: log.info,
 | 
								info: log.info,
 | 
				
			||||||
			userId: log.userId,
 | 
								userId: log.userId,
 | 
				
			||||||
			user: this.userEntityService.pack(log.user ?? log.userId, null, {
 | 
								user: hint?.packedUser ?? this.userEntityService.pack(log.user ?? log.userId, null, {
 | 
				
			||||||
				schema: 'UserDetailedNotMe',
 | 
									schema: 'UserDetailedNotMe',
 | 
				
			||||||
			}),
 | 
								}),
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@bindThis
 | 
						@bindThis
 | 
				
			||||||
	public packMany(
 | 
						public async packMany(
 | 
				
			||||||
		reports: any[],
 | 
							reports: MiModerationLog[],
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(reports.map(x => this.pack(x)));
 | 
							const _users = reports.map(({ user, userId }) => user ?? userId);
 | 
				
			||||||
 | 
							const _userMap = await this.userEntityService.packMany(_users, null, { schema: 'UserDetailedNotMe' })
 | 
				
			||||||
 | 
								.then(users => new Map(users.map(u => [u.id, u])));
 | 
				
			||||||
 | 
							return Promise.all(reports.map(report => this.pack(report, { packedUser: _userMap.get(report.userId) })));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,6 +30,9 @@ export class MutingEntityService {
 | 
				
			||||||
	public async pack(
 | 
						public async pack(
 | 
				
			||||||
		src: MiMuting['id'] | MiMuting,
 | 
							src: MiMuting['id'] | MiMuting,
 | 
				
			||||||
		me?: { id: MiUser['id'] } | null | undefined,
 | 
							me?: { id: MiUser['id'] } | null | undefined,
 | 
				
			||||||
 | 
							hints?: {
 | 
				
			||||||
 | 
								packedMutee?: Packed<'UserDetailedNotMe'>,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	): Promise<Packed<'Muting'>> {
 | 
						): Promise<Packed<'Muting'>> {
 | 
				
			||||||
		const muting = typeof src === 'object' ? src : await this.mutingsRepository.findOneByOrFail({ id: src });
 | 
							const muting = typeof src === 'object' ? src : await this.mutingsRepository.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,18 +41,21 @@ export class MutingEntityService {
 | 
				
			||||||
			createdAt: this.idService.parse(muting.id).date.toISOString(),
 | 
								createdAt: this.idService.parse(muting.id).date.toISOString(),
 | 
				
			||||||
			expiresAt: muting.expiresAt ? muting.expiresAt.toISOString() : null,
 | 
								expiresAt: muting.expiresAt ? muting.expiresAt.toISOString() : null,
 | 
				
			||||||
			muteeId: muting.muteeId,
 | 
								muteeId: muting.muteeId,
 | 
				
			||||||
			mutee: this.userEntityService.pack(muting.muteeId, me, {
 | 
								mutee: hints?.packedMutee ?? this.userEntityService.pack(muting.muteeId, me, {
 | 
				
			||||||
				schema: 'UserDetailedNotMe',
 | 
									schema: 'UserDetailedNotMe',
 | 
				
			||||||
			}),
 | 
								}),
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@bindThis
 | 
						@bindThis
 | 
				
			||||||
	public packMany(
 | 
						public async packMany(
 | 
				
			||||||
		mutings: any[],
 | 
							mutings: MiMuting[],
 | 
				
			||||||
		me: { id: MiUser['id'] },
 | 
							me: { id: MiUser['id'] },
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(mutings.map(x => this.pack(x, me)));
 | 
							const _mutees = mutings.map(({ mutee, muteeId }) => mutee ?? muteeId);
 | 
				
			||||||
 | 
							const _userMap = await this.userEntityService.packMany(_mutees, me, { schema: 'UserDetailedNotMe' })
 | 
				
			||||||
 | 
								.then(users => new Map(users.map(u => [u.id, u])));
 | 
				
			||||||
 | 
							return Promise.all(mutings.map(muting => this.pack(muting, me, { packedMutee: _userMap.get(muting.muteeId) })));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -290,6 +290,7 @@ export class NoteEntityService implements OnModuleInit {
 | 
				
			||||||
			_hint_?: {
 | 
								_hint_?: {
 | 
				
			||||||
				myReactions: Map<MiNote['id'], string | null>;
 | 
									myReactions: Map<MiNote['id'], string | null>;
 | 
				
			||||||
				packedFiles: Map<MiNote['fileIds'][number], Packed<'DriveFile'> | null>;
 | 
									packedFiles: Map<MiNote['fileIds'][number], Packed<'DriveFile'> | null>;
 | 
				
			||||||
 | 
									packedUsers: Map<MiUser['id'], Packed<'UserLite'>>
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	): Promise<Packed<'Note'>> {
 | 
						): Promise<Packed<'Note'>> {
 | 
				
			||||||
| 
						 | 
					@ -319,12 +320,13 @@ export class NoteEntityService implements OnModuleInit {
 | 
				
			||||||
			.filter(x => x.startsWith(':') && x.includes('@') && !x.includes('@.')) // リモートカスタム絵文字のみ
 | 
								.filter(x => x.startsWith(':') && x.includes('@') && !x.includes('@.')) // リモートカスタム絵文字のみ
 | 
				
			||||||
			.map(x => this.reactionService.decodeReaction(x).reaction.replaceAll(':', ''));
 | 
								.map(x => this.reactionService.decodeReaction(x).reaction.replaceAll(':', ''));
 | 
				
			||||||
		const packedFiles = options?._hint_?.packedFiles;
 | 
							const packedFiles = options?._hint_?.packedFiles;
 | 
				
			||||||
 | 
							const packedUsers = options?._hint_?.packedUsers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const packed: Packed<'Note'> = await awaitAll({
 | 
							const packed: Packed<'Note'> = await awaitAll({
 | 
				
			||||||
			id: note.id,
 | 
								id: note.id,
 | 
				
			||||||
			createdAt: this.idService.parse(note.id).date.toISOString(),
 | 
								createdAt: this.idService.parse(note.id).date.toISOString(),
 | 
				
			||||||
			userId: note.userId,
 | 
								userId: note.userId,
 | 
				
			||||||
			user: this.userEntityService.pack(note.user ?? note.userId, me),
 | 
								user: packedUsers?.get(note.userId) ?? this.userEntityService.pack(note.user ?? note.userId, me),
 | 
				
			||||||
			text: text,
 | 
								text: text,
 | 
				
			||||||
			cw: note.cw,
 | 
								cw: note.cw,
 | 
				
			||||||
			visibility: note.visibility,
 | 
								visibility: note.visibility,
 | 
				
			||||||
| 
						 | 
					@ -449,12 +451,20 @@ export class NoteEntityService implements OnModuleInit {
 | 
				
			||||||
		// TODO: 本当は renote とか reply がないのに renoteId とか replyId があったらここで解決しておく
 | 
							// TODO: 本当は renote とか reply がないのに renoteId とか replyId があったらここで解決しておく
 | 
				
			||||||
		const fileIds = notes.map(n => [n.fileIds, n.renote?.fileIds, n.reply?.fileIds]).flat(2).filter(isNotNull);
 | 
							const fileIds = notes.map(n => [n.fileIds, n.renote?.fileIds, n.reply?.fileIds]).flat(2).filter(isNotNull);
 | 
				
			||||||
		const packedFiles = fileIds.length > 0 ? await this.driveFileEntityService.packManyByIdsMap(fileIds) : new Map();
 | 
							const packedFiles = fileIds.length > 0 ? await this.driveFileEntityService.packManyByIdsMap(fileIds) : new Map();
 | 
				
			||||||
 | 
							const users = [
 | 
				
			||||||
 | 
								...notes.map(({ user, userId }) => user ?? userId),
 | 
				
			||||||
 | 
								...notes.map(({ replyUserId }) => replyUserId).filter(isNotNull),
 | 
				
			||||||
 | 
								...notes.map(({ renoteUserId }) => renoteUserId).filter(isNotNull),
 | 
				
			||||||
 | 
							];
 | 
				
			||||||
 | 
							const packedUsers = await this.userEntityService.packMany(users, me)
 | 
				
			||||||
 | 
								.then(users => new Map(users.map(u => [u.id, u])));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return await Promise.all(notes.map(n => this.pack(n, me, {
 | 
							return await Promise.all(notes.map(n => this.pack(n, me, {
 | 
				
			||||||
			...options,
 | 
								...options,
 | 
				
			||||||
			_hint_: {
 | 
								_hint_: {
 | 
				
			||||||
				myReactions: myReactionsMap,
 | 
									myReactions: myReactionsMap,
 | 
				
			||||||
				packedFiles,
 | 
									packedFiles,
 | 
				
			||||||
 | 
									packedUsers,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		})));
 | 
							})));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,6 +52,9 @@ export class NoteReactionEntityService implements OnModuleInit {
 | 
				
			||||||
		options?: {
 | 
							options?: {
 | 
				
			||||||
			withNote: boolean;
 | 
								withNote: boolean;
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							hints?: {
 | 
				
			||||||
 | 
								packedUser?: Packed<'UserLite'>
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	): Promise<Packed<'NoteReaction'>> {
 | 
						): Promise<Packed<'NoteReaction'>> {
 | 
				
			||||||
		const opts = Object.assign({
 | 
							const opts = Object.assign({
 | 
				
			||||||
			withNote: false,
 | 
								withNote: false,
 | 
				
			||||||
| 
						 | 
					@ -62,7 +65,7 @@ export class NoteReactionEntityService implements OnModuleInit {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			id: reaction.id,
 | 
								id: reaction.id,
 | 
				
			||||||
			createdAt: this.idService.parse(reaction.id).date.toISOString(),
 | 
								createdAt: this.idService.parse(reaction.id).date.toISOString(),
 | 
				
			||||||
			user: await this.userEntityService.pack(reaction.user ?? reaction.userId, me),
 | 
								user: hints?.packedUser ?? await this.userEntityService.pack(reaction.user ?? reaction.userId, me),
 | 
				
			||||||
			type: this.reactionService.convertLegacyReaction(reaction.reaction),
 | 
								type: this.reactionService.convertLegacyReaction(reaction.reaction),
 | 
				
			||||||
			...(opts.withNote ? {
 | 
								...(opts.withNote ? {
 | 
				
			||||||
				note: await this.noteEntityService.pack(reaction.note ?? reaction.noteId, me),
 | 
									note: await this.noteEntityService.pack(reaction.note ?? reaction.noteId, me),
 | 
				
			||||||
| 
						 | 
					@ -81,7 +84,9 @@ export class NoteReactionEntityService implements OnModuleInit {
 | 
				
			||||||
		const opts = Object.assign({
 | 
							const opts = Object.assign({
 | 
				
			||||||
			withNote: false,
 | 
								withNote: false,
 | 
				
			||||||
		}, options);
 | 
							}, options);
 | 
				
			||||||
 | 
							const _users = reactions.map(({ user, userId }) => user ?? userId);
 | 
				
			||||||
		return Promise.all(reactions.map(reaction => this.pack(reaction, me, opts)));
 | 
							const _userMap = await this.userEntityService.packMany(_users, me)
 | 
				
			||||||
 | 
								.then(users => new Map(users.map(u => [u.id, u])));
 | 
				
			||||||
 | 
							return Promise.all(reactions.map(reaction => this.pack(reaction, me, opts, { packedUser: _userMap.get(reaction.userId) })));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -40,6 +40,9 @@ export class PageEntityService {
 | 
				
			||||||
	public async pack(
 | 
						public async pack(
 | 
				
			||||||
		src: MiPage['id'] | MiPage,
 | 
							src: MiPage['id'] | MiPage,
 | 
				
			||||||
		me?: { id: MiUser['id'] } | null | undefined,
 | 
							me?: { id: MiUser['id'] } | null | undefined,
 | 
				
			||||||
 | 
							hint?: {
 | 
				
			||||||
 | 
								packedUser?: Packed<'UserLite'>
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	): Promise<Packed<'Page'>> {
 | 
						): Promise<Packed<'Page'>> {
 | 
				
			||||||
		const meId = me ? me.id : null;
 | 
							const meId = me ? me.id : null;
 | 
				
			||||||
		const page = typeof src === 'object' ? src : await this.pagesRepository.findOneByOrFail({ id: src });
 | 
							const page = typeof src === 'object' ? src : await this.pagesRepository.findOneByOrFail({ id: src });
 | 
				
			||||||
| 
						 | 
					@ -91,7 +94,7 @@ export class PageEntityService {
 | 
				
			||||||
			createdAt: this.idService.parse(page.id).date.toISOString(),
 | 
								createdAt: this.idService.parse(page.id).date.toISOString(),
 | 
				
			||||||
			updatedAt: page.updatedAt.toISOString(),
 | 
								updatedAt: page.updatedAt.toISOString(),
 | 
				
			||||||
			userId: page.userId,
 | 
								userId: page.userId,
 | 
				
			||||||
			user: this.userEntityService.pack(page.user ?? page.userId, me), // { schema: 'UserDetailed' } すると無限ループするので注意
 | 
								user: hint?.packedUser ?? this.userEntityService.pack(page.user ?? page.userId, me), // { schema: 'UserDetailed' } すると無限ループするので注意
 | 
				
			||||||
			content: page.content,
 | 
								content: page.content,
 | 
				
			||||||
			variables: page.variables,
 | 
								variables: page.variables,
 | 
				
			||||||
			title: page.title,
 | 
								title: page.title,
 | 
				
			||||||
| 
						 | 
					@ -110,11 +113,14 @@ export class PageEntityService {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@bindThis
 | 
						@bindThis
 | 
				
			||||||
	public packMany(
 | 
						public async packMany(
 | 
				
			||||||
		pages: MiPage[],
 | 
							pages: MiPage[],
 | 
				
			||||||
		me?: { id: MiUser['id'] } | null | undefined,
 | 
							me?: { id: MiUser['id'] } | null | undefined,
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(pages.map(x => this.pack(x, me)));
 | 
							const _users = pages.map(({ user, userId }) => user ?? userId);
 | 
				
			||||||
 | 
							const _userMap = await this.userEntityService.packMany(_users, me)
 | 
				
			||||||
 | 
								.then(users => new Map(users.map(u => [u.id, u])));
 | 
				
			||||||
 | 
							return Promise.all(pages.map(page => this.pack(page, me, { packedUser: _userMap.get(page.userId) })));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,6 +30,9 @@ export class RenoteMutingEntityService {
 | 
				
			||||||
	public async pack(
 | 
						public async pack(
 | 
				
			||||||
		src: MiRenoteMuting['id'] | MiRenoteMuting,
 | 
							src: MiRenoteMuting['id'] | MiRenoteMuting,
 | 
				
			||||||
		me?: { id: MiUser['id'] } | null | undefined,
 | 
							me?: { id: MiUser['id'] } | null | undefined,
 | 
				
			||||||
 | 
							hints?: {
 | 
				
			||||||
 | 
								packedMutee?: Packed<'UserDetailedNotMe'>
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	): Promise<Packed<'RenoteMuting'>> {
 | 
						): Promise<Packed<'RenoteMuting'>> {
 | 
				
			||||||
		const muting = typeof src === 'object' ? src : await this.renoteMutingsRepository.findOneByOrFail({ id: src });
 | 
							const muting = typeof src === 'object' ? src : await this.renoteMutingsRepository.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,18 +40,21 @@ export class RenoteMutingEntityService {
 | 
				
			||||||
			id: muting.id,
 | 
								id: muting.id,
 | 
				
			||||||
			createdAt: this.idService.parse(muting.id).date.toISOString(),
 | 
								createdAt: this.idService.parse(muting.id).date.toISOString(),
 | 
				
			||||||
			muteeId: muting.muteeId,
 | 
								muteeId: muting.muteeId,
 | 
				
			||||||
			mutee: this.userEntityService.pack(muting.muteeId, me, {
 | 
								mutee: hints?.packedMutee ?? this.userEntityService.pack(muting.muteeId, me, {
 | 
				
			||||||
				schema: 'UserDetailedNotMe',
 | 
									schema: 'UserDetailedNotMe',
 | 
				
			||||||
			}),
 | 
								}),
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@bindThis
 | 
						@bindThis
 | 
				
			||||||
	public packMany(
 | 
						public async packMany(
 | 
				
			||||||
		mutings: any[],
 | 
							mutings: MiRenoteMuting[],
 | 
				
			||||||
		me: { id: MiUser['id'] },
 | 
							me: { id: MiUser['id'] },
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(mutings.map(x => this.pack(x, me)));
 | 
							const _users = mutings.map(({ mutee, muteeId }) => mutee ?? muteeId);
 | 
				
			||||||
 | 
							const _userMap = await this.userEntityService.packMany(_users, me, { schema: 'UserDetailedNotMe' })
 | 
				
			||||||
 | 
								.then(users => new Map(users.map(u => [u.id, u])));
 | 
				
			||||||
 | 
							return Promise.all(mutings.map(muting => this.pack(muting, me, { packedMutee: _userMap.get(muting.muteeId) })));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,13 +28,15 @@ export class ReversiGameEntityService {
 | 
				
			||||||
	@bindThis
 | 
						@bindThis
 | 
				
			||||||
	public async packDetail(
 | 
						public async packDetail(
 | 
				
			||||||
		src: MiReversiGame['id'] | MiReversiGame,
 | 
							src: MiReversiGame['id'] | MiReversiGame,
 | 
				
			||||||
 | 
							hint?: {
 | 
				
			||||||
 | 
								packedUser1?: Packed<'UserLite'>,
 | 
				
			||||||
 | 
								packedUser2?: Packed<'UserLite'>,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	): Promise<Packed<'ReversiGameDetailed'>> {
 | 
						): Promise<Packed<'ReversiGameDetailed'>> {
 | 
				
			||||||
		const game = typeof src === 'object' ? src : await this.reversiGamesRepository.findOneByOrFail({ id: src });
 | 
							const game = typeof src === 'object' ? src : await this.reversiGamesRepository.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const users = await Promise.all([
 | 
							const user1 = hint?.packedUser1 ?? await this.userEntityService.pack(game.user1 ?? game.user1Id);
 | 
				
			||||||
			this.userEntityService.pack(game.user1 ?? game.user1Id),
 | 
							const user2 = hint?.packedUser2 ?? await this.userEntityService.pack(game.user2 ?? game.user2Id);
 | 
				
			||||||
			this.userEntityService.pack(game.user2 ?? game.user2Id),
 | 
					 | 
				
			||||||
		]);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return await awaitAll({
 | 
							return await awaitAll({
 | 
				
			||||||
			id: game.id,
 | 
								id: game.id,
 | 
				
			||||||
| 
						 | 
					@ -49,10 +51,10 @@ export class ReversiGameEntityService {
 | 
				
			||||||
			user2Ready: game.user2Ready,
 | 
								user2Ready: game.user2Ready,
 | 
				
			||||||
			user1Id: game.user1Id,
 | 
								user1Id: game.user1Id,
 | 
				
			||||||
			user2Id: game.user2Id,
 | 
								user2Id: game.user2Id,
 | 
				
			||||||
			user1: users[0],
 | 
								user1,
 | 
				
			||||||
			user2: users[1],
 | 
								user2,
 | 
				
			||||||
			winnerId: game.winnerId,
 | 
								winnerId: game.winnerId,
 | 
				
			||||||
			winner: game.winnerId ? users.find(u => u.id === game.winnerId)! : null,
 | 
								winner: game.winnerId ? [user1, user2].find(u => u.id === game.winnerId)! : null,
 | 
				
			||||||
			surrenderedUserId: game.surrenderedUserId,
 | 
								surrenderedUserId: game.surrenderedUserId,
 | 
				
			||||||
			timeoutUserId: game.timeoutUserId,
 | 
								timeoutUserId: game.timeoutUserId,
 | 
				
			||||||
			black: game.black,
 | 
								black: game.black,
 | 
				
			||||||
| 
						 | 
					@ -68,22 +70,35 @@ export class ReversiGameEntityService {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@bindThis
 | 
						@bindThis
 | 
				
			||||||
	public packDetailMany(
 | 
						public async packDetailMany(
 | 
				
			||||||
		xs: MiReversiGame[],
 | 
							games: MiReversiGame[],
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(xs.map(x => this.packDetail(x)));
 | 
							const _user1s = games.map(({ user1, user1Id }) => user1 ?? user1Id);
 | 
				
			||||||
 | 
							const _user2s = games.map(({ user2, user2Id }) => user2 ?? user2Id);
 | 
				
			||||||
 | 
							const _userMap = await this.userEntityService.packMany([..._user1s, ..._user2s])
 | 
				
			||||||
 | 
								.then(users => new Map(users.map(u => [u.id, u])));
 | 
				
			||||||
 | 
							return Promise.all(
 | 
				
			||||||
 | 
								games.map(game => {
 | 
				
			||||||
 | 
									return this.packDetail(game, {
 | 
				
			||||||
 | 
										packedUser1: _userMap.get(game.user1Id),
 | 
				
			||||||
 | 
										packedUser2: _userMap.get(game.user2Id),
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
								}),
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@bindThis
 | 
						@bindThis
 | 
				
			||||||
	public async packLite(
 | 
						public async packLite(
 | 
				
			||||||
		src: MiReversiGame['id'] | MiReversiGame,
 | 
							src: MiReversiGame['id'] | MiReversiGame,
 | 
				
			||||||
 | 
							hint?: {
 | 
				
			||||||
 | 
								packedUser1?: Packed<'UserLite'>,
 | 
				
			||||||
 | 
								packedUser2?: Packed<'UserLite'>,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	): Promise<Packed<'ReversiGameLite'>> {
 | 
						): Promise<Packed<'ReversiGameLite'>> {
 | 
				
			||||||
		const game = typeof src === 'object' ? src : await this.reversiGamesRepository.findOneByOrFail({ id: src });
 | 
							const game = typeof src === 'object' ? src : await this.reversiGamesRepository.findOneByOrFail({ id: src });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const users = await Promise.all([
 | 
							const user1 = hint?.packedUser1 ?? await this.userEntityService.pack(game.user1 ?? game.user1Id);
 | 
				
			||||||
			this.userEntityService.pack(game.user1 ?? game.user1Id),
 | 
							const user2 = hint?.packedUser2 ?? await this.userEntityService.pack(game.user2 ?? game.user2Id);
 | 
				
			||||||
			this.userEntityService.pack(game.user2 ?? game.user2Id),
 | 
					 | 
				
			||||||
		]);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return await awaitAll({
 | 
							return await awaitAll({
 | 
				
			||||||
			id: game.id,
 | 
								id: game.id,
 | 
				
			||||||
| 
						 | 
					@ -94,10 +109,10 @@ export class ReversiGameEntityService {
 | 
				
			||||||
			isEnded: game.isEnded,
 | 
								isEnded: game.isEnded,
 | 
				
			||||||
			user1Id: game.user1Id,
 | 
								user1Id: game.user1Id,
 | 
				
			||||||
			user2Id: game.user2Id,
 | 
								user2Id: game.user2Id,
 | 
				
			||||||
			user1: users[0],
 | 
								user1,
 | 
				
			||||||
			user2: users[1],
 | 
								user2,
 | 
				
			||||||
			winnerId: game.winnerId,
 | 
								winnerId: game.winnerId,
 | 
				
			||||||
			winner: game.winnerId ? users.find(u => u.id === game.winnerId)! : null,
 | 
								winner: game.winnerId ? [user1, user2].find(u => u.id === game.winnerId)! : null,
 | 
				
			||||||
			surrenderedUserId: game.surrenderedUserId,
 | 
								surrenderedUserId: game.surrenderedUserId,
 | 
				
			||||||
			timeoutUserId: game.timeoutUserId,
 | 
								timeoutUserId: game.timeoutUserId,
 | 
				
			||||||
			black: game.black,
 | 
								black: game.black,
 | 
				
			||||||
| 
						 | 
					@ -111,10 +126,21 @@ export class ReversiGameEntityService {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@bindThis
 | 
						@bindThis
 | 
				
			||||||
	public packLiteMany(
 | 
						public async packLiteMany(
 | 
				
			||||||
		xs: MiReversiGame[],
 | 
							games: MiReversiGame[],
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		return Promise.all(xs.map(x => this.packLite(x)));
 | 
							const _user1s = games.map(({ user1, user1Id }) => user1 ?? user1Id);
 | 
				
			||||||
 | 
							const _user2s = games.map(({ user2, user2Id }) => user2 ?? user2Id);
 | 
				
			||||||
 | 
							const _userMap = await this.userEntityService.packMany([..._user1s, ..._user2s])
 | 
				
			||||||
 | 
								.then(users => new Map(users.map(u => [u.id, u])));
 | 
				
			||||||
 | 
							return Promise.all(
 | 
				
			||||||
 | 
								games.map(game => {
 | 
				
			||||||
 | 
									return this.packLite(game, {
 | 
				
			||||||
 | 
										packedUser1: _userMap.get(game.user1Id),
 | 
				
			||||||
 | 
										packedUser2: _userMap.get(game.user2Id),
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
								}),
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -50,11 +50,14 @@ export class UserListEntityService {
 | 
				
			||||||
	public async packMembershipsMany(
 | 
						public async packMembershipsMany(
 | 
				
			||||||
		memberships: MiUserListMembership[],
 | 
							memberships: MiUserListMembership[],
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
 | 
							const _users = memberships.map(({ user, userId }) => user ?? userId);
 | 
				
			||||||
 | 
							const _userMap = await this.userEntityService.packMany(_users)
 | 
				
			||||||
 | 
								.then(users => new Map(users.map(u => [u.id, u])));
 | 
				
			||||||
		return Promise.all(memberships.map(async x => ({
 | 
							return Promise.all(memberships.map(async x => ({
 | 
				
			||||||
			id: x.id,
 | 
								id: x.id,
 | 
				
			||||||
			createdAt: this.idService.parse(x.id).date.toISOString(),
 | 
								createdAt: this.idService.parse(x.id).date.toISOString(),
 | 
				
			||||||
			userId: x.userId,
 | 
								userId: x.userId,
 | 
				
			||||||
			user: await this.userEntityService.pack(x.userId),
 | 
								user: _userMap.get(x.userId) ?? await this.userEntityService.pack(x.userId),
 | 
				
			||||||
			withReplies: x.withReplies,
 | 
								withReplies: x.withReplies,
 | 
				
			||||||
		})));
 | 
							})));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -89,10 +89,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 | 
				
			||||||
				.limit(ps.limit)
 | 
									.limit(ps.limit)
 | 
				
			||||||
				.getMany();
 | 
									.getMany();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								const _users = assigns.map(({ user, userId }) => user ?? userId);
 | 
				
			||||||
 | 
								const _userMap = await this.userEntityService.packMany(_users, me, { schema: 'UserDetailed' })
 | 
				
			||||||
 | 
									.then(users => new Map(users.map(u => [u.id, u])));
 | 
				
			||||||
			return await Promise.all(assigns.map(async assign => ({
 | 
								return await Promise.all(assigns.map(async assign => ({
 | 
				
			||||||
				id: assign.id,
 | 
									id: assign.id,
 | 
				
			||||||
				createdAt: this.idService.parse(assign.id).date.toISOString(),
 | 
									createdAt: this.idService.parse(assign.id).date.toISOString(),
 | 
				
			||||||
				user: await this.userEntityService.pack(assign.user!, me, { schema: 'UserDetailed' }),
 | 
									user: _userMap.get(assign.userId) ?? await this.userEntityService.pack(assign.user!, me, { schema: 'UserDetailed' }),
 | 
				
			||||||
				expiresAt: assign.expiresAt?.toISOString() ?? null,
 | 
									expiresAt: assign.expiresAt?.toISOString() ?? null,
 | 
				
			||||||
			})));
 | 
								})));
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -54,7 +54,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 | 
				
			||||||
				folderId: ps.folderId ?? IsNull(),
 | 
									folderId: ps.folderId ?? IsNull(),
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			return await Promise.all(files.map(file => this.driveFileEntityService.pack(file, { self: true })));
 | 
								return await this.driveFileEntityService.packMany(files, { self: true });
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -71,7 +71,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 | 
				
			||||||
				.limit(ps.limit)
 | 
									.limit(ps.limit)
 | 
				
			||||||
				.getMany();
 | 
									.getMany();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			return await Promise.all(requests.map(req => this.followRequestEntityService.pack(req)));
 | 
								return await this.followRequestEntityService.packMany(requests, me);
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -76,7 +76,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			const reactions = await query.limit(ps.limit).getMany();
 | 
								const reactions = await query.limit(ps.limit).getMany();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			return await Promise.all(reactions.map(reaction => this.noteReactionEntityService.pack(reaction, me)));
 | 
								return await this.noteReactionEntityService.packMany(reactions, me);
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -92,9 +92,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 | 
				
			||||||
				.limit(ps.limit)
 | 
									.limit(ps.limit)
 | 
				
			||||||
				.getMany();
 | 
									.getMany();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								const _users = assigns.map(({ user, userId }) => user ?? userId);
 | 
				
			||||||
 | 
								const _userMap = await this.userEntityService.packMany(_users, me, { schema: 'UserDetailed' })
 | 
				
			||||||
 | 
									.then(users => new Map(users.map(u => [u.id, u])));
 | 
				
			||||||
			return await Promise.all(assigns.map(async assign => ({
 | 
								return await Promise.all(assigns.map(async assign => ({
 | 
				
			||||||
				id: assign.id,
 | 
									id: assign.id,
 | 
				
			||||||
				user: await this.userEntityService.pack(assign.user!, me, { schema: 'UserDetailed' }),
 | 
									user: _userMap.get(assign.userId) ?? await this.userEntityService.pack(assign.user!, me, { schema: 'UserDetailed' }),
 | 
				
			||||||
			})));
 | 
								})));
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -118,12 +118,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 | 
				
			||||||
			const repliedUsersSorted = Object.keys(repliedUsers).sort((a, b) => repliedUsers[b] - repliedUsers[a]);
 | 
								const repliedUsersSorted = Object.keys(repliedUsers).sort((a, b) => repliedUsers[b] - repliedUsers[a]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Extract top replied users
 | 
								// Extract top replied users
 | 
				
			||||||
			const topRepliedUsers = repliedUsersSorted.slice(0, ps.limit);
 | 
								const topRepliedUserIds = repliedUsersSorted.slice(0, ps.limit);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Make replies object (includes weights)
 | 
								// Make replies object (includes weights)
 | 
				
			||||||
			const repliesObj = await Promise.all(topRepliedUsers.map(async (user) => ({
 | 
								const _userMap = await this.userEntityService.packMany(topRepliedUserIds, me, { schema: 'UserDetailed' })
 | 
				
			||||||
				user: await this.userEntityService.pack(user, me, { schema: 'UserDetailed' }),
 | 
									.then(users => new Map(users.map(u => [u.id, u])));
 | 
				
			||||||
				weight: repliedUsers[user] / peak,
 | 
								const repliesObj = await Promise.all(topRepliedUserIds.map(async (userId) => ({
 | 
				
			||||||
 | 
									user: _userMap.get(userId) ?? await this.userEntityService.pack(userId, me, { schema: 'UserDetailed' }),
 | 
				
			||||||
 | 
									weight: repliedUsers[userId] / peak,
 | 
				
			||||||
			})));
 | 
								})));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			return repliesObj;
 | 
								return repliesObj;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -117,9 +117,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 | 
				
			||||||
					if (user != null) _users.push(user);
 | 
										if (user != null) _users.push(user);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				return await Promise.all(_users.map(u => this.userEntityService.pack(u, me, {
 | 
									const _userMap = await this.userEntityService.packMany(_users, me, { schema: 'UserDetailed' })
 | 
				
			||||||
					schema: 'UserDetailed',
 | 
										.then(users => new Map(users.map(u => [u.id, u])));
 | 
				
			||||||
				})));
 | 
									return _users.map(u => _userMap.get(u.id)!);
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				// Lookup user
 | 
									// Lookup user
 | 
				
			||||||
				if (typeof ps.host === 'string' && typeof ps.username === 'string') {
 | 
									if (typeof ps.host === 'string' && typeof ps.username === 'string') {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue