From 5aa58da9180f95fc787d175b9d074d8c22357cf0 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Wed, 13 Feb 2019 16:33:07 +0900
Subject: [PATCH] Migrate cafy to 14.0 (#4240)

---
 package.json                                  |  2 +-
 src/misc/cafy-id.ts                           | 18 +++-
 src/server/activitypub/followers.ts           |  4 +-
 src/server/activitypub/following.ts           |  4 +-
 src/server/activitypub/outbox.ts              |  6 +-
 .../api/endpoints/admin/abuse-user-reports.ts |  6 +-
 src/server/api/endpoints/admin/drive/files.ts |  8 +-
 src/server/api/endpoints/admin/emoji/add.ts   |  2 +-
 src/server/api/endpoints/admin/emoji/list.ts  |  2 +-
 src/server/api/endpoints/admin/show-users.ts  | 10 +--
 src/server/api/endpoints/admin/update-meta.ts | 88 +++++++++----------
 src/server/api/endpoints/app/create.ts        |  2 +-
 src/server/api/endpoints/blocking/list.ts     |  6 +-
 .../api/endpoints/charts/active-users.ts      |  2 +-
 src/server/api/endpoints/charts/drive.ts      |  2 +-
 src/server/api/endpoints/charts/federation.ts |  2 +-
 src/server/api/endpoints/charts/hashtag.ts    |  2 +-
 src/server/api/endpoints/charts/instance.ts   |  2 +-
 src/server/api/endpoints/charts/network.ts    |  2 +-
 src/server/api/endpoints/charts/notes.ts      |  2 +-
 src/server/api/endpoints/charts/user/drive.ts |  2 +-
 .../api/endpoints/charts/user/following.ts    |  2 +-
 src/server/api/endpoints/charts/user/notes.ts |  2 +-
 .../api/endpoints/charts/user/reactions.ts    |  2 +-
 src/server/api/endpoints/charts/users.ts      |  2 +-
 src/server/api/endpoints/drive/files.ts       | 10 +--
 .../api/endpoints/drive/files/create.ts       |  6 +-
 src/server/api/endpoints/drive/files/find.ts  |  2 +-
 src/server/api/endpoints/drive/files/show.ts  |  4 +-
 .../api/endpoints/drive/files/update.ts       |  6 +-
 .../endpoints/drive/files/upload_from_url.ts  |  6 +-
 src/server/api/endpoints/drive/folders.ts     |  8 +-
 .../api/endpoints/drive/folders/create.ts     |  4 +-
 .../api/endpoints/drive/folders/find.ts       |  2 +-
 .../api/endpoints/drive/folders/update.ts     |  4 +-
 src/server/api/endpoints/drive/stream.ts      |  8 +-
 .../api/endpoints/federation/instances.ts     | 12 +--
 .../api/endpoints/games/reversi/games.ts      |  8 +-
 src/server/api/endpoints/hashtags/search.ts   |  4 +-
 src/server/api/endpoints/i/authorized_apps.ts |  6 +-
 src/server/api/endpoints/i/favorites.ts       |  6 +-
 src/server/api/endpoints/i/notifications.ts   | 14 +--
 src/server/api/endpoints/i/signin_history.ts  |  6 +-
 src/server/api/endpoints/i/update.ts          | 30 +++----
 .../api/endpoints/i/update_client_setting.ts  |  2 +-
 src/server/api/endpoints/i/update_email.ts    |  2 +-
 src/server/api/endpoints/messaging/history.ts |  2 +-
 .../api/endpoints/messaging/messages.ts       |  8 +-
 .../endpoints/messaging/messages/create.ts    |  4 +-
 src/server/api/endpoints/meta.ts              |  2 +-
 src/server/api/endpoints/mute/list.ts         |  6 +-
 src/server/api/endpoints/my/apps.ts           |  4 +-
 src/server/api/endpoints/notes.ts             | 18 ++--
 .../api/endpoints/notes/conversation.ts       |  4 +-
 src/server/api/endpoints/notes/create.ts      | 44 +++++-----
 src/server/api/endpoints/notes/featured.ts    |  2 +-
 .../api/endpoints/notes/global-timeline.ts    | 14 +--
 .../api/endpoints/notes/hybrid-timeline.ts    | 20 ++---
 .../api/endpoints/notes/local-timeline.ts     | 18 ++--
 src/server/api/endpoints/notes/mentions.ts    | 10 +--
 .../endpoints/notes/polls/recommendation.ts   |  4 +-
 src/server/api/endpoints/notes/reactions.ts   |  8 +-
 src/server/api/endpoints/notes/renotes.ts     |  6 +-
 src/server/api/endpoints/notes/replies.ts     |  4 +-
 src/server/api/endpoints/notes/search.ts      |  4 +-
 .../api/endpoints/notes/search_by_tag.ts      | 28 +++---
 src/server/api/endpoints/notes/timeline.ts    | 20 ++---
 .../api/endpoints/notes/user-list-timeline.ts | 20 ++---
 src/server/api/endpoints/users.ts             |  8 +-
 src/server/api/endpoints/users/followers.ts   |  6 +-
 src/server/api/endpoints/users/following.ts   |  6 +-
 .../users/get_frequently_replied_users.ts     |  2 +-
 src/server/api/endpoints/users/notes.ts       | 32 +++----
 .../api/endpoints/users/recommendation.ts     |  4 +-
 src/server/api/endpoints/users/search.ts      |  8 +-
 src/server/api/endpoints/users/show.ts        |  8 +-
 src/server/web/docs.ts                        | 23 -----
 77 files changed, 334 insertions(+), 345 deletions(-)

diff --git a/package.json b/package.json
index ea1332dd6f..61b6a08180 100644
--- a/package.json
+++ b/package.json
@@ -103,7 +103,7 @@
 		"bcryptjs": "2.4.3",
 		"bee-queue": "1.2.2",
 		"bootstrap-vue": "2.0.0-rc.11",
-		"cafy": "12.1.0",
+		"cafy": "14.0.1",
 		"chai": "4.2.0",
 		"chai-http": "4.2.1",
 		"chalk": "2.4.2",
diff --git a/src/misc/cafy-id.ts b/src/misc/cafy-id.ts
index b99a27ee4a..eb5822a7d4 100644
--- a/src/misc/cafy-id.ts
+++ b/src/misc/cafy-id.ts
@@ -25,9 +25,9 @@ export type ObjectId = mongo.ObjectID;
 /**
  * ID
  */
-export default class ID extends Context<string> {
-	constructor() {
-		super();
+export default class ID<Maybe = string> extends Context<string | Maybe> {
+	constructor(optional = false, nullable = false) {
+		super(optional, nullable);
 
 		this.push((v: any) => {
 			if (!isObjectId(v) && isNotAnId(v)) {
@@ -40,4 +40,16 @@ export default class ID extends Context<string> {
 	public getType() {
 		return super.getType('string');
 	}
+
+	public makeOptional(): ID<undefined> {
+		return new ID(true, false);
+	}
+
+	public makeNullable(): ID<null> {
+		return new ID(false, true);
+	}
+
+	public makeOptionalNullable(): ID<undefined | null> {
+		return new ID(true, true);
+	}
 }
diff --git a/src/server/activitypub/followers.ts b/src/server/activitypub/followers.ts
index ce7fe94e42..bf5066008d 100644
--- a/src/server/activitypub/followers.ts
+++ b/src/server/activitypub/followers.ts
@@ -20,10 +20,10 @@ export default async (ctx: Router.IRouterContext) => {
 	const userId = new ObjectID(ctx.params.user);
 
 	// Get 'cursor' parameter
-	const [cursor = null, cursorErr] = $.type(ID).optional.get(ctx.request.query.cursor);
+	const [cursor = null, cursorErr] = $.optional.type(ID).get(ctx.request.query.cursor);
 
 	// Get 'page' parameter
-	const pageErr = !$.str.optional.or(['true', 'false']).ok(ctx.request.query.page);
+	const pageErr = !$.optional.str.or(['true', 'false']).ok(ctx.request.query.page);
 	const page: boolean = ctx.request.query.page === 'true';
 
 	// Validate parameters
diff --git a/src/server/activitypub/following.ts b/src/server/activitypub/following.ts
index da65ee79fa..cb86546f1c 100644
--- a/src/server/activitypub/following.ts
+++ b/src/server/activitypub/following.ts
@@ -20,10 +20,10 @@ export default async (ctx: Router.IRouterContext) => {
 	const userId = new ObjectID(ctx.params.user);
 
 	// Get 'cursor' parameter
-	const [cursor = null, cursorErr] = $.type(ID).optional.get(ctx.request.query.cursor);
+	const [cursor = null, cursorErr] = $.optional.type(ID).get(ctx.request.query.cursor);
 
 	// Get 'page' parameter
-	const pageErr = !$.str.optional.or(['true', 'false']).ok(ctx.request.query.page);
+	const pageErr = !$.optional.str.or(['true', 'false']).ok(ctx.request.query.page);
 	const page: boolean = ctx.request.query.page === 'true';
 
 	// Validate parameters
diff --git a/src/server/activitypub/outbox.ts b/src/server/activitypub/outbox.ts
index 41f832aefa..508e7e5ec9 100644
--- a/src/server/activitypub/outbox.ts
+++ b/src/server/activitypub/outbox.ts
@@ -24,13 +24,13 @@ export default async (ctx: Router.IRouterContext) => {
 	const userId = new ObjectID(ctx.params.user);
 
 	// Get 'sinceId' parameter
-	const [sinceId, sinceIdErr] = $.type(ID).optional.get(ctx.request.query.since_id);
+	const [sinceId, sinceIdErr] = $.optional.type(ID).get(ctx.request.query.since_id);
 
 	// Get 'untilId' parameter
-	const [untilId, untilIdErr] = $.type(ID).optional.get(ctx.request.query.until_id);
+	const [untilId, untilIdErr] = $.optional.type(ID).get(ctx.request.query.until_id);
 
 	// Get 'page' parameter
-	const pageErr = !$.str.optional.or(['true', 'false']).ok(ctx.request.query.page);
+	const pageErr = !$.optional.str.or(['true', 'false']).ok(ctx.request.query.page);
 	const page: boolean = ctx.request.query.page === 'true';
 
 	// Validate parameters
diff --git a/src/server/api/endpoints/admin/abuse-user-reports.ts b/src/server/api/endpoints/admin/abuse-user-reports.ts
index 0de3d3e8fa..fc95dd4bb1 100644
--- a/src/server/api/endpoints/admin/abuse-user-reports.ts
+++ b/src/server/api/endpoints/admin/abuse-user-reports.ts
@@ -9,17 +9,17 @@ export const meta = {
 
 	params: {
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		sinceId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		untilId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 	}
diff --git a/src/server/api/endpoints/admin/drive/files.ts b/src/server/api/endpoints/admin/drive/files.ts
index 177a808cbf..12b2bac376 100644
--- a/src/server/api/endpoints/admin/drive/files.ts
+++ b/src/server/api/endpoints/admin/drive/files.ts
@@ -8,17 +8,17 @@ export const meta = {
 
 	params: {
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		offset: {
-			validator: $.num.optional.min(0),
+			validator: $.optional.num.min(0),
 			default: 0
 		},
 
 		sort: {
-			validator: $.str.optional.or([
+			validator: $.optional.str.or([
 				'+createdAt',
 				'-createdAt',
 				'+size',
@@ -27,7 +27,7 @@ export const meta = {
 		},
 
 		origin: {
-			validator: $.str.optional.or([
+			validator: $.optional.str.or([
 				'combined',
 				'local',
 				'remote',
diff --git a/src/server/api/endpoints/admin/emoji/add.ts b/src/server/api/endpoints/admin/emoji/add.ts
index 91b5ff62d7..cab3841649 100644
--- a/src/server/api/endpoints/admin/emoji/add.ts
+++ b/src/server/api/endpoints/admin/emoji/add.ts
@@ -20,7 +20,7 @@ export const meta = {
 		},
 
 		aliases: {
-			validator: $.arr($.str.min(1)).optional,
+			validator: $.optional.arr($.str.min(1)),
 			default: [] as string[]
 		}
 	}
diff --git a/src/server/api/endpoints/admin/emoji/list.ts b/src/server/api/endpoints/admin/emoji/list.ts
index 428b274fed..624fa7845c 100644
--- a/src/server/api/endpoints/admin/emoji/list.ts
+++ b/src/server/api/endpoints/admin/emoji/list.ts
@@ -12,7 +12,7 @@ export const meta = {
 
 	params: {
 		host: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 			default: null as any
 		}
 	}
diff --git a/src/server/api/endpoints/admin/show-users.ts b/src/server/api/endpoints/admin/show-users.ts
index e883b25e79..cc21b4e672 100644
--- a/src/server/api/endpoints/admin/show-users.ts
+++ b/src/server/api/endpoints/admin/show-users.ts
@@ -8,17 +8,17 @@ export const meta = {
 
 	params: {
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		offset: {
-			validator: $.num.optional.min(0),
+			validator: $.optional.num.min(0),
 			default: 0
 		},
 
 		sort: {
-			validator: $.str.optional.or([
+			validator: $.optional.str.or([
 				'+follower',
 				'-follower',
 				'+createdAt',
@@ -29,7 +29,7 @@ export const meta = {
 		},
 
 		state: {
-			validator: $.str.optional.or([
+			validator: $.optional.str.or([
 				'all',
 				'admin',
 				'moderator',
@@ -42,7 +42,7 @@ export const meta = {
 		},
 
 		origin: {
-			validator: $.str.optional.or([
+			validator: $.optional.str.or([
 				'combined',
 				'local',
 				'remote',
diff --git a/src/server/api/endpoints/admin/update-meta.ts b/src/server/api/endpoints/admin/update-meta.ts
index 13663243a2..5e893c7d3b 100644
--- a/src/server/api/endpoints/admin/update-meta.ts
+++ b/src/server/api/endpoints/admin/update-meta.ts
@@ -12,84 +12,84 @@ export const meta = {
 
 	params: {
 		broadcasts: {
-			validator: $.arr($.obj()).optional.nullable,
+			validator: $.optional.nullable.arr($.obj()),
 			desc: {
 				'ja-JP': 'ブロードキャスト'
 			}
 		},
 
 		disableRegistration: {
-			validator: $.bool.optional.nullable,
+			validator: $.optional.nullable.bool,
 			desc: {
 				'ja-JP': '招待制か否か'
 			}
 		},
 
 		disableLocalTimeline: {
-			validator: $.bool.optional.nullable,
+			validator: $.optional.nullable.bool,
 			desc: {
 				'ja-JP': 'ローカルタイムライン(とソーシャルタイムライン)を無効にするか否か'
 			}
 		},
 
 		disableGlobalTimeline: {
-			validator: $.bool.optional.nullable,
+			validator: $.optional.nullable.bool,
 			desc: {
 				'ja-JP': 'グローバルタイムラインを無効にするか否か'
 			}
 		},
 
 		hidedTags: {
-			validator: $.arr($.str).optional.nullable,
+			validator: $.optional.nullable.arr($.str),
 			desc: {
 				'ja-JP': '統計などで無視するハッシュタグ'
 			}
 		},
 
 		mascotImageUrl: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 			desc: {
 				'ja-JP': 'インスタンスキャラクター画像のURL'
 			}
 		},
 
 		bannerUrl: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 			desc: {
 				'ja-JP': 'インスタンスのバナー画像URL'
 			}
 		},
 
 		errorImageUrl: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 			desc: {
 				'ja-JP': 'インスタンスのエラー画像URL'
 			}
 		},
 
 		name: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 			desc: {
 				'ja-JP': 'インスタンス名'
 			}
 		},
 
 		description: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 			desc: {
 				'ja-JP': 'インスタンスの紹介文'
 			}
 		},
 
 		maxNoteTextLength: {
-			validator: $.num.optional.min(1),
+			validator: $.optional.num.min(1),
 			desc: {
 				'ja-JP': '投稿の最大文字数'
 			}
 		},
 
 		localDriveCapacityMb: {
-			validator: $.num.optional.min(0),
+			validator: $.optional.num.min(0),
 			desc: {
 				'ja-JP': 'ローカルユーザーひとりあたりのドライブ容量 (メガバイト単位)',
 				'en-US': 'Drive capacity of a local user (MB)'
@@ -97,7 +97,7 @@ export const meta = {
 		},
 
 		remoteDriveCapacityMb: {
-			validator: $.num.optional.min(0),
+			validator: $.optional.num.min(0),
 			desc: {
 				'ja-JP': 'リモートユーザーひとりあたりのドライブ容量 (メガバイト単位)',
 				'en-US': 'Drive capacity of a remote user (MB)'
@@ -105,217 +105,217 @@ export const meta = {
 		},
 
 		cacheRemoteFiles: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'リモートのファイルをキャッシュするか否か'
 			}
 		},
 
 		enableRecaptcha: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'reCAPTCHAを使用するか否か'
 			}
 		},
 
 		recaptchaSiteKey: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 			desc: {
 				'ja-JP': 'reCAPTCHA site key'
 			}
 		},
 
 		recaptchaSecretKey: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 			desc: {
 				'ja-JP': 'reCAPTCHA secret key'
 			}
 		},
 
 		proxyAccount: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 			desc: {
 				'ja-JP': 'プロキシアカウントのユーザー名'
 			}
 		},
 
 		maintainerName: {
-			validator: $.str.optional,
+			validator: $.optional.str,
 			desc: {
 				'ja-JP': 'インスタンスの管理者名'
 			}
 		},
 
 		maintainerEmail: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 			desc: {
 				'ja-JP': 'インスタンス管理者の連絡先メールアドレス'
 			}
 		},
 
 		langs: {
-			validator: $.arr($.str).optional,
+			validator: $.optional.arr($.str),
 			desc: {
 				'ja-JP': 'インスタンスの対象言語'
 			}
 		},
 
 		summalyProxy: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 			desc: {
 				'ja-JP': 'summalyプロキシURL'
 			}
 		},
 
 		enableTwitterIntegration: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'Twitter連携機能を有効にするか否か'
 			}
 		},
 
 		twitterConsumerKey: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 			desc: {
 				'ja-JP': 'TwitterアプリのConsumer key'
 			}
 		},
 
 		twitterConsumerSecret: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 			desc: {
 				'ja-JP': 'TwitterアプリのConsumer secret'
 			}
 		},
 
 		enableGithubIntegration: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'GitHub連携機能を有効にするか否か'
 			}
 		},
 
 		githubClientId: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 			desc: {
 				'ja-JP': 'GitHubアプリのClient ID'
 			}
 		},
 
 		githubClientSecret: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 			desc: {
 				'ja-JP': 'GitHubアプリのClient Secret'
 			}
 		},
 
 		enableDiscordIntegration: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'Discord連携機能を有効にするか否か'
 			}
 		},
 
 		discordClientId: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 			desc: {
 				'ja-JP': 'DiscordアプリのClient ID'
 			}
 		},
 
 		discordClientSecret: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 			desc: {
 				'ja-JP': 'DiscordアプリのClient Secret'
 			}
 		},
 
 		enableExternalUserRecommendation: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': '外部ユーザーレコメンデーションを有効にする'
 			}
 		},
 
 		externalUserRecommendationEngine: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 			desc: {
 				'ja-JP': '外部ユーザーレコメンデーションのサードパーティエンジン'
 			}
 		},
 
 		externalUserRecommendationTimeout: {
-			validator: $.num.optional.nullable.min(0),
+			validator: $.optional.nullable.num.min(0),
 			desc: {
 				'ja-JP': '外部ユーザーレコメンデーションのタイムアウト (ミリ秒)'
 			}
 		},
 
 		enableEmail: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'メール配信を有効にするか否か'
 			}
 		},
 
 		email: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 			desc: {
 				'ja-JP': 'メール配信する際に利用するメールアドレス'
 			}
 		},
 
 		smtpSecure: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'SMTPサーバがSSLを使用しているか否か'
 			}
 		},
 
 		smtpHost: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 			desc: {
 				'ja-JP': 'SMTPサーバのホスト'
 			}
 		},
 
 		smtpPort: {
-			validator: $.num.optional.nullable,
+			validator: $.optional.nullable.num,
 			desc: {
 				'ja-JP': 'SMTPサーバのポート'
 			}
 		},
 
 		smtpUser: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 			desc: {
 				'ja-JP': 'SMTPサーバのユーザー名'
 			}
 		},
 
 		smtpPass: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 			desc: {
 				'ja-JP': 'SMTPサーバのパスワード'
 			}
 		},
 
 		enableServiceWorker: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'ServiceWorkerを有効にするか否か'
 			}
 		},
 
 		swPublicKey: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 			desc: {
 				'ja-JP': 'ServiceWorkerのVAPIDキーペアの公開鍵'
 			}
 		},
 
 		swPrivateKey: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 			desc: {
 				'ja-JP': 'ServiceWorkerのVAPIDキーペアの秘密鍵'
 			}
diff --git a/src/server/api/endpoints/app/create.ts b/src/server/api/endpoints/app/create.ts
index 4766668eaa..849530c20d 100644
--- a/src/server/api/endpoints/app/create.ts
+++ b/src/server/api/endpoints/app/create.ts
@@ -21,7 +21,7 @@ export const meta = {
 
 		// TODO: Check it is valid url
 		callbackUrl: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 			default: null as any
 		},
 	}
diff --git a/src/server/api/endpoints/blocking/list.ts b/src/server/api/endpoints/blocking/list.ts
index 0413c32b7a..79eaaece8a 100644
--- a/src/server/api/endpoints/blocking/list.ts
+++ b/src/server/api/endpoints/blocking/list.ts
@@ -15,17 +15,17 @@ export const meta = {
 
 	params: {
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 30
 		},
 
 		sinceId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		untilId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 	}
diff --git a/src/server/api/endpoints/charts/active-users.ts b/src/server/api/endpoints/charts/active-users.ts
index 828edd5e92..9adf7bd388 100644
--- a/src/server/api/endpoints/charts/active-users.ts
+++ b/src/server/api/endpoints/charts/active-users.ts
@@ -18,7 +18,7 @@ export const meta = {
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 500),
+			validator: $.optional.num.range(1, 500),
 			default: 30,
 			desc: {
 				'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
diff --git a/src/server/api/endpoints/charts/drive.ts b/src/server/api/endpoints/charts/drive.ts
index 072aec6726..0f1106e123 100644
--- a/src/server/api/endpoints/charts/drive.ts
+++ b/src/server/api/endpoints/charts/drive.ts
@@ -18,7 +18,7 @@ export const meta = {
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 500),
+			validator: $.optional.num.range(1, 500),
 			default: 30,
 			desc: {
 				'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
diff --git a/src/server/api/endpoints/charts/federation.ts b/src/server/api/endpoints/charts/federation.ts
index 3a433e7aa2..6b9cfc4510 100644
--- a/src/server/api/endpoints/charts/federation.ts
+++ b/src/server/api/endpoints/charts/federation.ts
@@ -18,7 +18,7 @@ export const meta = {
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 500),
+			validator: $.optional.num.range(1, 500),
 			default: 30,
 			desc: {
 				'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
diff --git a/src/server/api/endpoints/charts/hashtag.ts b/src/server/api/endpoints/charts/hashtag.ts
index 496afae991..e1127fe388 100644
--- a/src/server/api/endpoints/charts/hashtag.ts
+++ b/src/server/api/endpoints/charts/hashtag.ts
@@ -18,7 +18,7 @@ export const meta = {
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 500),
+			validator: $.optional.num.range(1, 500),
 			default: 30,
 			desc: {
 				'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
diff --git a/src/server/api/endpoints/charts/instance.ts b/src/server/api/endpoints/charts/instance.ts
index 99e7d9c1e1..9b5f33024a 100644
--- a/src/server/api/endpoints/charts/instance.ts
+++ b/src/server/api/endpoints/charts/instance.ts
@@ -18,7 +18,7 @@ export const meta = {
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 500),
+			validator: $.optional.num.range(1, 500),
 			default: 30,
 			desc: {
 				'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
diff --git a/src/server/api/endpoints/charts/network.ts b/src/server/api/endpoints/charts/network.ts
index 7dd7f905f3..db13f13030 100644
--- a/src/server/api/endpoints/charts/network.ts
+++ b/src/server/api/endpoints/charts/network.ts
@@ -18,7 +18,7 @@ export const meta = {
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 500),
+			validator: $.optional.num.range(1, 500),
 			default: 30,
 			desc: {
 				'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
diff --git a/src/server/api/endpoints/charts/notes.ts b/src/server/api/endpoints/charts/notes.ts
index 5019e1ec9e..4877df6080 100644
--- a/src/server/api/endpoints/charts/notes.ts
+++ b/src/server/api/endpoints/charts/notes.ts
@@ -18,7 +18,7 @@ export const meta = {
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 500),
+			validator: $.optional.num.range(1, 500),
 			default: 30,
 			desc: {
 				'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
diff --git a/src/server/api/endpoints/charts/user/drive.ts b/src/server/api/endpoints/charts/user/drive.ts
index cea8ac6fa3..556651d335 100644
--- a/src/server/api/endpoints/charts/user/drive.ts
+++ b/src/server/api/endpoints/charts/user/drive.ts
@@ -19,7 +19,7 @@ export const meta = {
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 500),
+			validator: $.optional.num.range(1, 500),
 			default: 30,
 			desc: {
 				'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
diff --git a/src/server/api/endpoints/charts/user/following.ts b/src/server/api/endpoints/charts/user/following.ts
index 63f96dd241..ccc83952f3 100644
--- a/src/server/api/endpoints/charts/user/following.ts
+++ b/src/server/api/endpoints/charts/user/following.ts
@@ -19,7 +19,7 @@ export const meta = {
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 500),
+			validator: $.optional.num.range(1, 500),
 			default: 30,
 			desc: {
 				'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
diff --git a/src/server/api/endpoints/charts/user/notes.ts b/src/server/api/endpoints/charts/user/notes.ts
index 109e32e540..3296771c68 100644
--- a/src/server/api/endpoints/charts/user/notes.ts
+++ b/src/server/api/endpoints/charts/user/notes.ts
@@ -19,7 +19,7 @@ export const meta = {
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 500),
+			validator: $.optional.num.range(1, 500),
 			default: 30,
 			desc: {
 				'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
diff --git a/src/server/api/endpoints/charts/user/reactions.ts b/src/server/api/endpoints/charts/user/reactions.ts
index bf46b708ba..be228b5f7d 100644
--- a/src/server/api/endpoints/charts/user/reactions.ts
+++ b/src/server/api/endpoints/charts/user/reactions.ts
@@ -19,7 +19,7 @@ export const meta = {
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 500),
+			validator: $.optional.num.range(1, 500),
 			default: 30,
 			desc: {
 				'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
diff --git a/src/server/api/endpoints/charts/users.ts b/src/server/api/endpoints/charts/users.ts
index 593fc7c197..e474e29970 100644
--- a/src/server/api/endpoints/charts/users.ts
+++ b/src/server/api/endpoints/charts/users.ts
@@ -18,7 +18,7 @@ export const meta = {
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 500),
+			validator: $.optional.num.range(1, 500),
 			default: 30,
 			desc: {
 				'ja-JP': '最大数。例えば 30 を指定したとすると、スパンが"day"の場合は30日分のデータが、スパンが"hour"の場合は30時間分のデータが返ります。'
diff --git a/src/server/api/endpoints/drive/files.ts b/src/server/api/endpoints/drive/files.ts
index 99e5e36ef3..6b96313910 100644
--- a/src/server/api/endpoints/drive/files.ts
+++ b/src/server/api/endpoints/drive/files.ts
@@ -15,28 +15,28 @@ export const meta = {
 
 	params: {
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		sinceId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		untilId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		folderId: {
-			validator: $.type(ID).optional.nullable,
+			validator: $.optional.nullable.type(ID),
 			default: null as any,
 			transform: transform,
 		},
 
 		type: {
-			validator: $.str.optional.match(/^[a-zA-Z\/\-\*]+$/)
+			validator: $.optional.str.match(/^[a-zA-Z\/\-\*]+$/)
 		}
 	}
 };
diff --git a/src/server/api/endpoints/drive/files/create.ts b/src/server/api/endpoints/drive/files/create.ts
index eacaa3f64f..3611652b6c 100644
--- a/src/server/api/endpoints/drive/files/create.ts
+++ b/src/server/api/endpoints/drive/files/create.ts
@@ -25,7 +25,7 @@ export const meta = {
 
 	params: {
 		folderId: {
-			validator: $.type(ID).optional.nullable,
+			validator: $.optional.nullable.type(ID),
 			transform: transform,
 			default: null as any,
 			desc: {
@@ -34,7 +34,7 @@ export const meta = {
 		},
 
 		isSensitive: {
-			validator: $.or($.bool, $.str).optional,
+			validator: $.optional.or($.bool, $.str),
 			default: false,
 			transform: (v: any): boolean => v === true || v === 'true',
 			desc: {
@@ -44,7 +44,7 @@ export const meta = {
 		},
 
 		force: {
-			validator: $.or($.bool, $.str).optional,
+			validator: $.optional.or($.bool, $.str),
 			default: false,
 			transform: (v: any): boolean => v === true || v === 'true',
 			desc: {
diff --git a/src/server/api/endpoints/drive/files/find.ts b/src/server/api/endpoints/drive/files/find.ts
index 691ad6123d..0ebd5b789e 100644
--- a/src/server/api/endpoints/drive/files/find.ts
+++ b/src/server/api/endpoints/drive/files/find.ts
@@ -14,7 +14,7 @@ export const meta = {
 		},
 
 		folderId: {
-			validator: $.type(ID).optional.nullable,
+			validator: $.optional.nullable.type(ID),
 			transform: transform,
 			default: null as any,
 			desc: {
diff --git a/src/server/api/endpoints/drive/files/show.ts b/src/server/api/endpoints/drive/files/show.ts
index e6d85a5efb..994583bd01 100644
--- a/src/server/api/endpoints/drive/files/show.ts
+++ b/src/server/api/endpoints/drive/files/show.ts
@@ -19,7 +19,7 @@ export const meta = {
 
 	params: {
 		fileId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 			desc: {
 				'ja-JP': '対象のファイルID',
@@ -28,7 +28,7 @@ export const meta = {
 		},
 
 		url: {
-			validator: $.str.optional,
+			validator: $.optional.str,
 			desc: {
 				'ja-JP': '対象のファイルのURL',
 				'en-US': 'Target file URL'
diff --git a/src/server/api/endpoints/drive/files/update.ts b/src/server/api/endpoints/drive/files/update.ts
index 4db493c1d0..79acbec3cd 100644
--- a/src/server/api/endpoints/drive/files/update.ts
+++ b/src/server/api/endpoints/drive/files/update.ts
@@ -26,7 +26,7 @@ export const meta = {
 		},
 
 		folderId: {
-			validator: $.type(ID).optional.nullable,
+			validator: $.optional.nullable.type(ID),
 			transform: transform,
 			default: undefined as any,
 			desc: {
@@ -35,7 +35,7 @@ export const meta = {
 		},
 
 		name: {
-			validator: $.str.optional.pipe(validateFileName),
+			validator: $.optional.str.pipe(validateFileName),
 			default: undefined as any,
 			desc: {
 				'ja-JP': 'ファイル名',
@@ -44,7 +44,7 @@ export const meta = {
 		},
 
 		isSensitive: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: undefined as any,
 			desc: {
 				'ja-JP': 'このメディアが「閲覧注意」(NSFW)かどうか',
diff --git a/src/server/api/endpoints/drive/files/upload_from_url.ts b/src/server/api/endpoints/drive/files/upload_from_url.ts
index c67df49ea0..d79b321735 100644
--- a/src/server/api/endpoints/drive/files/upload_from_url.ts
+++ b/src/server/api/endpoints/drive/files/upload_from_url.ts
@@ -26,13 +26,13 @@ export const meta = {
 		},
 
 		folderId: {
-			validator: $.type(ID).optional.nullable,
+			validator: $.optional.nullable.type(ID),
 			default: null as any,
 			transform: transform
 		},
 
 		isSensitive: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: false,
 			desc: {
 				'ja-JP': 'このメディアが「閲覧注意」(NSFW)かどうか',
@@ -41,7 +41,7 @@ export const meta = {
 		},
 
 		force: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: false,
 			desc: {
 				'ja-JP': 'true にすると、同じハッシュを持つファイルが既にアップロードされていても強制的にファイルを作成します。',
diff --git a/src/server/api/endpoints/drive/folders.ts b/src/server/api/endpoints/drive/folders.ts
index 7ada01d90b..bdbbf35ff1 100644
--- a/src/server/api/endpoints/drive/folders.ts
+++ b/src/server/api/endpoints/drive/folders.ts
@@ -15,22 +15,22 @@ export const meta = {
 
 	params: {
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		sinceId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		untilId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		folderId: {
-			validator: $.type(ID).optional.nullable,
+			validator: $.optional.nullable.type(ID),
 			default: null as any,
 			transform: transform,
 		}
diff --git a/src/server/api/endpoints/drive/folders/create.ts b/src/server/api/endpoints/drive/folders/create.ts
index 1aca521a76..f4b520be28 100644
--- a/src/server/api/endpoints/drive/folders/create.ts
+++ b/src/server/api/endpoints/drive/folders/create.ts
@@ -18,7 +18,7 @@ export const meta = {
 
 	params: {
 		name: {
-			validator: $.str.optional.pipe(isValidFolderName),
+			validator: $.optional.str.pipe(isValidFolderName),
 			default: 'Untitled',
 			desc: {
 				'ja-JP': 'フォルダ名',
@@ -27,7 +27,7 @@ export const meta = {
 		},
 
 		parentId: {
-			validator: $.type(ID).optional.nullable,
+			validator: $.optional.nullable.type(ID),
 			transform: transform,
 			desc: {
 				'ja-JP': '親フォルダID',
diff --git a/src/server/api/endpoints/drive/folders/find.ts b/src/server/api/endpoints/drive/folders/find.ts
index 453153c507..bbf496150a 100644
--- a/src/server/api/endpoints/drive/folders/find.ts
+++ b/src/server/api/endpoints/drive/folders/find.ts
@@ -14,7 +14,7 @@ export const meta = {
 		},
 
 		parentId: {
-			validator: $.type(ID).optional.nullable,
+			validator: $.optional.nullable.type(ID),
 			transform: transform,
 			default: null as any,
 			desc: {
diff --git a/src/server/api/endpoints/drive/folders/update.ts b/src/server/api/endpoints/drive/folders/update.ts
index 3207594367..9c515474cd 100644
--- a/src/server/api/endpoints/drive/folders/update.ts
+++ b/src/server/api/endpoints/drive/folders/update.ts
@@ -27,7 +27,7 @@ export const meta = {
 		},
 
 		name: {
-			validator: $.str.optional.pipe(isValidFolderName),
+			validator: $.optional.str.pipe(isValidFolderName),
 			desc: {
 				'ja-JP': 'フォルダ名',
 				'en-US': 'Folder name'
@@ -35,7 +35,7 @@ export const meta = {
 		},
 
 		parentId: {
-			validator: $.type(ID).optional.nullable,
+			validator: $.optional.nullable.type(ID),
 			transform: transform,
 			desc: {
 				'ja-JP': '親フォルダID',
diff --git a/src/server/api/endpoints/drive/stream.ts b/src/server/api/endpoints/drive/stream.ts
index ec373889b5..c48ed8c37c 100644
--- a/src/server/api/endpoints/drive/stream.ts
+++ b/src/server/api/endpoints/drive/stream.ts
@@ -10,22 +10,22 @@ export const meta = {
 
 	params: {
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		sinceId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		untilId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		type: {
-			validator: $.str.optional.match(/^[a-zA-Z\/\-\*]+$/)
+			validator: $.optional.str.match(/^[a-zA-Z\/\-\*]+$/)
 		}
 	}
 };
diff --git a/src/server/api/endpoints/federation/instances.ts b/src/server/api/endpoints/federation/instances.ts
index 9c1e57ee16..3df24cc84a 100644
--- a/src/server/api/endpoints/federation/instances.ts
+++ b/src/server/api/endpoints/federation/instances.ts
@@ -7,29 +7,29 @@ export const meta = {
 
 	params: {
 		blocked: {
-			validator: $.bool.optional.nullable,
+			validator: $.optional.nullable.bool,
 		},
 
 		notResponding: {
-			validator: $.bool.optional.nullable,
+			validator: $.optional.nullable.bool,
 		},
 
 		markedAsClosed: {
-			validator: $.bool.optional.nullable,
+			validator: $.optional.nullable.bool,
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 30
 		},
 
 		offset: {
-			validator: $.num.optional.min(0),
+			validator: $.optional.num.min(0),
 			default: 0
 		},
 
 		sort: {
-			validator: $.str.optional,
+			validator: $.optional.str,
 		}
 	}
 };
diff --git a/src/server/api/endpoints/games/reversi/games.ts b/src/server/api/endpoints/games/reversi/games.ts
index e0c4a2772d..fd9308f7e5 100644
--- a/src/server/api/endpoints/games/reversi/games.ts
+++ b/src/server/api/endpoints/games/reversi/games.ts
@@ -6,22 +6,22 @@ import define from '../../../define';
 export const meta = {
 	params: {
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		sinceId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		untilId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		my: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: false
 		}
 	}
diff --git a/src/server/api/endpoints/hashtags/search.ts b/src/server/api/endpoints/hashtags/search.ts
index d8a2156357..dee776c1f6 100644
--- a/src/server/api/endpoints/hashtags/search.ts
+++ b/src/server/api/endpoints/hashtags/search.ts
@@ -12,7 +12,7 @@ export const meta = {
 
 	params: {
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10,
 			desc: {
 				'ja-JP': '最大数'
@@ -27,7 +27,7 @@ export const meta = {
 		},
 
 		offset: {
-			validator: $.num.optional.min(0),
+			validator: $.optional.num.min(0),
 			default: 0,
 			desc: {
 				'ja-JP': 'オフセット'
diff --git a/src/server/api/endpoints/i/authorized_apps.ts b/src/server/api/endpoints/i/authorized_apps.ts
index c4b992b52f..3e60afe698 100644
--- a/src/server/api/endpoints/i/authorized_apps.ts
+++ b/src/server/api/endpoints/i/authorized_apps.ts
@@ -10,17 +10,17 @@ export const meta = {
 
 	params: {
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10,
 		},
 
 		offset: {
-			validator: $.num.optional.min(0),
+			validator: $.optional.num.min(0),
 			default: 0,
 		},
 
 		sort: {
-			validator: $.str.optional.or('desc|asc'),
+			validator: $.optional.str.or('desc|asc'),
 			default: 'desc',
 		}
 	}
diff --git a/src/server/api/endpoints/i/favorites.ts b/src/server/api/endpoints/i/favorites.ts
index 3bfed4cbbc..c476844b4b 100644
--- a/src/server/api/endpoints/i/favorites.ts
+++ b/src/server/api/endpoints/i/favorites.ts
@@ -15,17 +15,17 @@ export const meta = {
 
 	params: {
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		sinceId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		untilId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		}
 	}
diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts
index 264ed3c9ce..6287e42622 100644
--- a/src/server/api/endpoints/i/notifications.ts
+++ b/src/server/api/endpoints/i/notifications.ts
@@ -19,37 +19,37 @@ export const meta = {
 
 	params: {
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		sinceId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		untilId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		following: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: false
 		},
 
 		markAsRead: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: true
 		},
 
 		includeTypes: {
-			validator: $.arr($.str.or(['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'poll_vote', 'receiveFollowRequest'])).optional,
+			validator: $.optional.arr($.str.or(['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'poll_vote', 'receiveFollowRequest'])),
 			default: [] as string[]
 		},
 
 		excludeTypes: {
-			validator: $.arr($.str.or(['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'poll_vote', 'receiveFollowRequest'])).optional,
+			validator: $.optional.arr($.str.or(['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'poll_vote', 'receiveFollowRequest'])),
 			default: [] as string[]
 		}
 	}
diff --git a/src/server/api/endpoints/i/signin_history.ts b/src/server/api/endpoints/i/signin_history.ts
index c30e3f8fb8..cdffc6cc3c 100644
--- a/src/server/api/endpoints/i/signin_history.ts
+++ b/src/server/api/endpoints/i/signin_history.ts
@@ -10,17 +10,17 @@ export const meta = {
 
 	params: {
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		sinceId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		untilId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		}
 	}
diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts
index 0ad7e0f33f..6ae63c52db 100644
--- a/src/server/api/endpoints/i/update.ts
+++ b/src/server/api/endpoints/i/update.ts
@@ -24,42 +24,42 @@ export const meta = {
 
 	params: {
 		name: {
-			validator: $.str.optional.nullable.pipe(isValidName),
+			validator: $.optional.nullable.str.pipe(isValidName),
 			desc: {
 				'ja-JP': '名前(ハンドルネームやニックネーム)'
 			}
 		},
 
 		description: {
-			validator: $.str.optional.nullable.pipe(isValidDescription),
+			validator: $.optional.nullable.str.pipe(isValidDescription),
 			desc: {
 				'ja-JP': 'アカウントの説明や自己紹介'
 			}
 		},
 
 		lang: {
-			validator: $.str.optional.nullable.or(Object.keys(langmap)),
+			validator: $.optional.nullable.str.or(Object.keys(langmap)),
 			desc: {
 				'ja-JP': '言語'
 			}
 		},
 
 		location: {
-			validator: $.str.optional.nullable.pipe(isValidLocation),
+			validator: $.optional.nullable.str.pipe(isValidLocation),
 			desc: {
 				'ja-JP': '住んでいる地域、所在'
 			}
 		},
 
 		birthday: {
-			validator: $.str.optional.nullable.pipe(isValidBirthday),
+			validator: $.optional.nullable.str.pipe(isValidBirthday),
 			desc: {
 				'ja-JP': '誕生日 (YYYY-MM-DD形式)'
 			}
 		},
 
 		avatarId: {
-			validator: $.type(ID).optional.nullable,
+			validator: $.optional.nullable.type(ID),
 			transform: transform,
 			desc: {
 				'ja-JP': 'アイコンに設定する画像のドライブファイルID'
@@ -67,7 +67,7 @@ export const meta = {
 		},
 
 		bannerId: {
-			validator: $.type(ID).optional.nullable,
+			validator: $.optional.nullable.type(ID),
 			transform: transform,
 			desc: {
 				'ja-JP': 'バナーに設定する画像のドライブファイルID'
@@ -75,7 +75,7 @@ export const meta = {
 		},
 
 		wallpaperId: {
-			validator: $.type(ID).optional.nullable,
+			validator: $.optional.nullable.type(ID),
 			transform: transform,
 			desc: {
 				'ja-JP': '壁紙に設定する画像のドライブファイルID'
@@ -83,49 +83,49 @@ export const meta = {
 		},
 
 		isLocked: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': '鍵アカウントか否か'
 			}
 		},
 
 		carefulBot: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'Botからのフォローを承認制にするか'
 			}
 		},
 
 		autoAcceptFollowed: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'フォローしているユーザーからのフォローリクエストを自動承認するか'
 			}
 		},
 
 		isBot: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'Botか否か'
 			}
 		},
 
 		isCat: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': '猫か否か'
 			}
 		},
 
 		autoWatch: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': '投稿の自動ウォッチをするか否か'
 			}
 		},
 
 		alwaysMarkNsfw: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'アップロードするメディアをデフォルトで「閲覧注意」として設定するか'
 			}
diff --git a/src/server/api/endpoints/i/update_client_setting.ts b/src/server/api/endpoints/i/update_client_setting.ts
index 2b2f3d3c7b..2157986f10 100644
--- a/src/server/api/endpoints/i/update_client_setting.ts
+++ b/src/server/api/endpoints/i/update_client_setting.ts
@@ -14,7 +14,7 @@ export const meta = {
 		},
 
 		value: {
-			validator: $.any.nullable
+			validator: $.nullable.any
 		}
 	}
 };
diff --git a/src/server/api/endpoints/i/update_email.ts b/src/server/api/endpoints/i/update_email.ts
index c3aafc8d8b..8781c8ac93 100644
--- a/src/server/api/endpoints/i/update_email.ts
+++ b/src/server/api/endpoints/i/update_email.ts
@@ -26,7 +26,7 @@ export const meta = {
 		},
 
 		email: {
-			validator: $.str.optional.nullable
+			validator: $.optional.nullable.str
 		},
 	}
 };
diff --git a/src/server/api/endpoints/messaging/history.ts b/src/server/api/endpoints/messaging/history.ts
index 78abea269a..408e2d42a6 100644
--- a/src/server/api/endpoints/messaging/history.ts
+++ b/src/server/api/endpoints/messaging/history.ts
@@ -15,7 +15,7 @@ export const meta = {
 
 	params: {
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		}
 	}
diff --git a/src/server/api/endpoints/messaging/messages.ts b/src/server/api/endpoints/messaging/messages.ts
index 486bc56b98..4c1b7206d6 100644
--- a/src/server/api/endpoints/messaging/messages.ts
+++ b/src/server/api/endpoints/messaging/messages.ts
@@ -27,22 +27,22 @@ export const meta = {
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		sinceId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		untilId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		markAsRead: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: true
 		}
 	}
diff --git a/src/server/api/endpoints/messaging/messages/create.ts b/src/server/api/endpoints/messaging/messages/create.ts
index 8300f69e53..96a047a9bf 100644
--- a/src/server/api/endpoints/messaging/messages/create.ts
+++ b/src/server/api/endpoints/messaging/messages/create.ts
@@ -32,11 +32,11 @@ export const meta = {
 		},
 
 		text: {
-			validator: $.str.optional.pipe(isValidText)
+			validator: $.optional.str.pipe(isValidText)
 		},
 
 		fileId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		}
 	}
diff --git a/src/server/api/endpoints/meta.ts b/src/server/api/endpoints/meta.ts
index 91cb095c92..ebbaabc24e 100644
--- a/src/server/api/endpoints/meta.ts
+++ b/src/server/api/endpoints/meta.ts
@@ -20,7 +20,7 @@ export const meta = {
 
 	params: {
 		detail: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: true
 		}
 	},
diff --git a/src/server/api/endpoints/mute/list.ts b/src/server/api/endpoints/mute/list.ts
index 23c90ab9d2..ffe1f971d5 100644
--- a/src/server/api/endpoints/mute/list.ts
+++ b/src/server/api/endpoints/mute/list.ts
@@ -15,17 +15,17 @@ export const meta = {
 
 	params: {
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 30
 		},
 
 		sinceId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		untilId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 	}
diff --git a/src/server/api/endpoints/my/apps.ts b/src/server/api/endpoints/my/apps.ts
index 646ffcc263..0a95707396 100644
--- a/src/server/api/endpoints/my/apps.ts
+++ b/src/server/api/endpoints/my/apps.ts
@@ -12,12 +12,12 @@ export const meta = {
 
 	params: {
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		offset: {
-			validator: $.num.optional.min(0),
+			validator: $.optional.num.min(0),
 			default: 0
 		}
 	}
diff --git a/src/server/api/endpoints/notes.ts b/src/server/api/endpoints/notes.ts
index fce373f03e..64575ee6ed 100644
--- a/src/server/api/endpoints/notes.ts
+++ b/src/server/api/endpoints/notes.ts
@@ -10,59 +10,59 @@ export const meta = {
 
 	params: {
 		local: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'ローカルの投稿に限定するか否か'
 			}
 		},
 
 		reply: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': '返信に限定するか否か'
 			}
 		},
 
 		renote: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'Renoteに限定するか否か'
 			}
 		},
 
 		withFiles: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'ファイルが添付された投稿に限定するか否か'
 			}
 		},
 
 		media: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'ファイルが添付された投稿に限定するか否か (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
 			}
 		},
 
 		poll: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'アンケートが添付された投稿に限定するか否か'
 			}
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		sinceId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		untilId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 	}
diff --git a/src/server/api/endpoints/notes/conversation.ts b/src/server/api/endpoints/notes/conversation.ts
index 6a19832f93..848cf9e162 100644
--- a/src/server/api/endpoints/notes/conversation.ts
+++ b/src/server/api/endpoints/notes/conversation.ts
@@ -22,12 +22,12 @@ export const meta = {
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		offset: {
-			validator: $.num.optional.min(0),
+			validator: $.optional.num.min(0),
 			default: 0
 		},
 	}
diff --git a/src/server/api/endpoints/notes/create.ts b/src/server/api/endpoints/notes/create.ts
index 5dba01d9ab..47eebc186c 100644
--- a/src/server/api/endpoints/notes/create.ts
+++ b/src/server/api/endpoints/notes/create.ts
@@ -35,7 +35,7 @@ export const meta = {
 
 	params: {
 		visibility: {
-			validator: $.str.optional.or(['public', 'home', 'followers', 'specified', 'private']),
+			validator: $.optional.str.or(['public', 'home', 'followers', 'specified', 'private']),
 			default: 'public',
 			desc: {
 				'ja-JP': '投稿の公開範囲'
@@ -43,7 +43,7 @@ export const meta = {
 		},
 
 		visibleUserIds: {
-			validator: $.arr($.type(ID)).optional.unique().min(0),
+			validator: $.optional.arr($.type(ID)).unique().min(0),
 			transform: transformMany,
 			desc: {
 				'ja-JP': '(投稿の公開範囲が specified の場合)投稿を閲覧できるユーザー'
@@ -51,7 +51,7 @@ export const meta = {
 		},
 
 		text: {
-			validator: $.str.optional.nullable.pipe(text =>
+			validator: $.optional.nullable.str.pipe(text =>
 				length(text.trim()) <= maxNoteTextLength && text.trim() != ''
 			),
 			default: null as any,
@@ -61,14 +61,14 @@ export const meta = {
 		},
 
 		cw: {
-			validator: $.str.optional.nullable.pipe(isValidCw),
+			validator: $.optional.nullable.str.pipe(isValidCw),
 			desc: {
 				'ja-JP': 'コンテンツの警告。このパラメータを指定すると設定したテキストで投稿のコンテンツを隠す事が出来ます。'
 			}
 		},
 
 		viaMobile: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: false,
 			desc: {
 				'ja-JP': 'モバイルデバイスからの投稿か否か。'
@@ -76,7 +76,7 @@ export const meta = {
 		},
 
 		localOnly: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: false,
 			desc: {
 				'ja-JP': 'ローカルのみに投稿か否か。'
@@ -84,7 +84,7 @@ export const meta = {
 		},
 
 		noExtractMentions: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: false,
 			desc: {
 				'ja-JP': '本文からメンションを展開しないか否か。'
@@ -92,7 +92,7 @@ export const meta = {
 		},
 
 		noExtractHashtags: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: false,
 			desc: {
 				'ja-JP': '本文からハッシュタグを展開しないか否か。'
@@ -100,7 +100,7 @@ export const meta = {
 		},
 
 		noExtractEmojis: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: false,
 			desc: {
 				'ja-JP': '本文からカスタム絵文字を展開しないか否か。'
@@ -108,16 +108,16 @@ export const meta = {
 		},
 
 		geo: {
-			validator: $.obj({
+			validator: $.optional.nullable.obj({
 				coordinates: $.arr().length(2)
 					.item(0, $.num.range(-180, 180))
 					.item(1, $.num.range(-90, 90)),
-				altitude: $.num.nullable,
-				accuracy: $.num.nullable,
-				altitudeAccuracy: $.num.nullable,
-				heading: $.num.nullable.range(0, 360),
-				speed: $.num.nullable
-			}).optional.nullable.strict(),
+				altitude: $.nullable.num,
+				accuracy: $.nullable.num,
+				altitudeAccuracy: $.nullable.num,
+				heading: $.nullable.num.range(0, 360),
+				speed: $.nullable.num
+			}).strict(),
 			desc: {
 				'ja-JP': '位置情報'
 			},
@@ -125,7 +125,7 @@ export const meta = {
 		},
 
 		fileIds: {
-			validator: $.arr($.type(ID)).optional.unique().range(1, 4),
+			validator: $.optional.arr($.type(ID)).unique().range(1, 4),
 			transform: transformMany,
 			desc: {
 				'ja-JP': '添付するファイル'
@@ -133,7 +133,7 @@ export const meta = {
 		},
 
 		mediaIds: {
-			validator: $.arr($.type(ID)).optional.unique().range(1, 4),
+			validator: $.optional.arr($.type(ID)).unique().range(1, 4),
 			transform: transformMany,
 			desc: {
 				'ja-JP': '添付するファイル (このパラメータは廃止予定です。代わりに fileIds を使ってください。)'
@@ -141,7 +141,7 @@ export const meta = {
 		},
 
 		replyId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 			desc: {
 				'ja-JP': '返信対象'
@@ -149,7 +149,7 @@ export const meta = {
 		},
 
 		renoteId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 			desc: {
 				'ja-JP': 'Renote対象'
@@ -157,12 +157,12 @@ export const meta = {
 		},
 
 		poll: {
-			validator: $.obj({
+			validator: $.optional.obj({
 				choices: $.arr($.str)
 					.unique()
 					.range(2, 10)
 					.each(c => c.length > 0 && c.length < 50)
-			}).optional.strict(),
+			}).strict(),
 			desc: {
 				'ja-JP': 'アンケート'
 			},
diff --git a/src/server/api/endpoints/notes/featured.ts b/src/server/api/endpoints/notes/featured.ts
index 823137ac21..371d822127 100644
--- a/src/server/api/endpoints/notes/featured.ts
+++ b/src/server/api/endpoints/notes/featured.ts
@@ -13,7 +13,7 @@ export const meta = {
 
 	params: {
 		limit: {
-			validator: $.num.optional.range(1, 30),
+			validator: $.optional.num.range(1, 30),
 			default: 10,
 			desc: {
 				'ja-JP': '最大数'
diff --git a/src/server/api/endpoints/notes/global-timeline.ts b/src/server/api/endpoints/notes/global-timeline.ts
index 63b5b2d2b5..11825f288f 100644
--- a/src/server/api/endpoints/notes/global-timeline.ts
+++ b/src/server/api/endpoints/notes/global-timeline.ts
@@ -14,40 +14,40 @@ export const meta = {
 
 	params: {
 		withFiles: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'ファイルが添付された投稿に限定するか否か'
 			}
 		},
 
 		mediaOnly: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'ファイルが添付された投稿に限定するか否か (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
 			}
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		sinceId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		untilId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		sinceDate: {
-			validator: $.num.optional
+			validator: $.optional.num
 		},
 
 		untilDate: {
-			validator: $.num.optional
+			validator: $.optional.num
 		},
 	}
 };
diff --git a/src/server/api/endpoints/notes/hybrid-timeline.ts b/src/server/api/endpoints/notes/hybrid-timeline.ts
index f1ef7cea10..77ccf1abca 100644
--- a/src/server/api/endpoints/notes/hybrid-timeline.ts
+++ b/src/server/api/endpoints/notes/hybrid-timeline.ts
@@ -16,7 +16,7 @@ export const meta = {
 
 	params: {
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10,
 			desc: {
 				'ja-JP': '最大数'
@@ -24,7 +24,7 @@ export const meta = {
 		},
 
 		sinceId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 			desc: {
 				'ja-JP': '指定すると、この投稿を基点としてより新しい投稿を取得します'
@@ -32,7 +32,7 @@ export const meta = {
 		},
 
 		untilId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 			desc: {
 				'ja-JP': '指定すると、この投稿を基点としてより古い投稿を取得します'
@@ -40,21 +40,21 @@ export const meta = {
 		},
 
 		sinceDate: {
-			validator: $.num.optional,
+			validator: $.optional.num,
 			desc: {
 				'ja-JP': '指定した時間を基点としてより新しい投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
 			}
 		},
 
 		untilDate: {
-			validator: $.num.optional,
+			validator: $.optional.num,
 			desc: {
 				'ja-JP': '指定した時間を基点としてより古い投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
 			}
 		},
 
 		includeMyRenotes: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: true,
 			desc: {
 				'ja-JP': '自分の行ったRenoteを含めるかどうか'
@@ -62,7 +62,7 @@ export const meta = {
 		},
 
 		includeRenotedMyNotes: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: true,
 			desc: {
 				'ja-JP': 'Renoteされた自分の投稿を含めるかどうか'
@@ -70,7 +70,7 @@ export const meta = {
 		},
 
 		includeLocalRenotes: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: true,
 			desc: {
 				'ja-JP': 'Renoteされたローカルの投稿を含めるかどうか'
@@ -78,14 +78,14 @@ export const meta = {
 		},
 
 		withFiles: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します'
 			}
 		},
 
 		mediaOnly: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
 			}
diff --git a/src/server/api/endpoints/notes/local-timeline.ts b/src/server/api/endpoints/notes/local-timeline.ts
index d3bc7dc6e0..34695e1bca 100644
--- a/src/server/api/endpoints/notes/local-timeline.ts
+++ b/src/server/api/endpoints/notes/local-timeline.ts
@@ -15,28 +15,28 @@ export const meta = {
 
 	params: {
 		withFiles: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'ファイルが添付された投稿に限定するか否か'
 			}
 		},
 
 		mediaOnly: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'ファイルが添付された投稿に限定するか否か (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
 			}
 		},
 
 		fileType: {
-			validator: $.arr($.str).optional,
+			validator: $.optional.arr($.str),
 			desc: {
 				'ja-JP': '指定された種類のファイルが添付された投稿のみを取得します'
 			}
 		},
 
 		excludeNsfw: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: false,
 			desc: {
 				'ja-JP': 'true にすると、NSFW指定されたファイルを除外します(fileTypeが指定されている場合のみ有効)'
@@ -44,26 +44,26 @@ export const meta = {
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		sinceId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		untilId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		sinceDate: {
-			validator: $.num.optional,
+			validator: $.optional.num,
 		},
 
 		untilDate: {
-			validator: $.num.optional,
+			validator: $.optional.num,
 		},
 	}
 };
diff --git a/src/server/api/endpoints/notes/mentions.ts b/src/server/api/endpoints/notes/mentions.ts
index c8def2aef7..3935a93599 100644
--- a/src/server/api/endpoints/notes/mentions.ts
+++ b/src/server/api/endpoints/notes/mentions.ts
@@ -17,27 +17,27 @@ export const meta = {
 
 	params: {
 		following: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: false
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		sinceId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		untilId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		visibility: {
-			validator: $.str.optional,
+			validator: $.optional.str,
 		},
 	}
 };
diff --git a/src/server/api/endpoints/notes/polls/recommendation.ts b/src/server/api/endpoints/notes/polls/recommendation.ts
index 2fe0a78970..8e11e65296 100644
--- a/src/server/api/endpoints/notes/polls/recommendation.ts
+++ b/src/server/api/endpoints/notes/polls/recommendation.ts
@@ -14,12 +14,12 @@ export const meta = {
 
 	params: {
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		offset: {
-			validator: $.num.optional.min(0),
+			validator: $.optional.num.min(0),
 			default: 0
 		}
 	}
diff --git a/src/server/api/endpoints/notes/reactions.ts b/src/server/api/endpoints/notes/reactions.ts
index 4486ce2c70..54cbd23379 100644
--- a/src/server/api/endpoints/notes/reactions.ts
+++ b/src/server/api/endpoints/notes/reactions.ts
@@ -23,22 +23,22 @@ export const meta = {
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		offset: {
-			validator: $.num.optional,
+			validator: $.optional.num,
 			default: 0
 		},
 
 		sinceId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		untilId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 	}
diff --git a/src/server/api/endpoints/notes/renotes.ts b/src/server/api/endpoints/notes/renotes.ts
index d9009e1598..531953540f 100644
--- a/src/server/api/endpoints/notes/renotes.ts
+++ b/src/server/api/endpoints/notes/renotes.ts
@@ -22,17 +22,17 @@ export const meta = {
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		sinceId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		},
 
 		untilId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 		}
 	}
diff --git a/src/server/api/endpoints/notes/replies.ts b/src/server/api/endpoints/notes/replies.ts
index b455610773..26e9c6c5c9 100644
--- a/src/server/api/endpoints/notes/replies.ts
+++ b/src/server/api/endpoints/notes/replies.ts
@@ -24,12 +24,12 @@ export const meta = {
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		offset: {
-			validator: $.num.optional.min(0),
+			validator: $.optional.num.min(0),
 			default: 0
 		},
 	}
diff --git a/src/server/api/endpoints/notes/search.ts b/src/server/api/endpoints/notes/search.ts
index 652dc6b31d..e671e72504 100644
--- a/src/server/api/endpoints/notes/search.ts
+++ b/src/server/api/endpoints/notes/search.ts
@@ -20,12 +20,12 @@ export const meta = {
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		offset: {
-			validator: $.num.optional.min(0),
+			validator: $.optional.num.min(0),
 			default: 0
 		}
 	}
diff --git a/src/server/api/endpoints/notes/search_by_tag.ts b/src/server/api/endpoints/notes/search_by_tag.ts
index b9a1b6b1b8..05b22bb9f0 100644
--- a/src/server/api/endpoints/notes/search_by_tag.ts
+++ b/src/server/api/endpoints/notes/search_by_tag.ts
@@ -13,31 +13,31 @@ export const meta = {
 
 	params: {
 		tag: {
-			validator: $.str.optional,
+			validator: $.optional.str,
 			desc: {
 				'ja-JP': 'タグ'
 			}
 		},
 
 		query: {
-			validator: $.arr($.arr($.str)).optional,
+			validator: $.optional.arr($.arr($.str)),
 			desc: {
 				'ja-JP': 'クエリ'
 			}
 		},
 
 		following: {
-			validator: $.bool.optional.nullable,
+			validator: $.optional.nullable.bool,
 			default: null as any
 		},
 
 		mute: {
-			validator: $.str.optional,
+			validator: $.optional.str,
 			default: 'mute_all'
 		},
 
 		reply: {
-			validator: $.bool.optional.nullable,
+			validator: $.optional.nullable.bool,
 			default: null as any,
 			desc: {
 				'ja-JP': '返信に限定するか否か'
@@ -45,7 +45,7 @@ export const meta = {
 		},
 
 		renote: {
-			validator: $.bool.optional.nullable,
+			validator: $.optional.nullable.bool,
 			default: null as any,
 			desc: {
 				'ja-JP': 'Renoteに限定するか否か'
@@ -53,14 +53,14 @@ export const meta = {
 		},
 
 		withFiles: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します'
 			}
 		},
 
 		media: {
-			validator: $.bool.optional.nullable,
+			validator: $.optional.nullable.bool,
 			default: null as any,
 			desc: {
 				'ja-JP': 'ファイルが添付された投稿に限定するか否か (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
@@ -68,7 +68,7 @@ export const meta = {
 		},
 
 		poll: {
-			validator: $.bool.optional.nullable,
+			validator: $.optional.nullable.bool,
 			default: null as any,
 			desc: {
 				'ja-JP': 'アンケートが添付された投稿に限定するか否か'
@@ -76,7 +76,7 @@ export const meta = {
 		},
 
 		untilId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 			desc: {
 				'ja-JP': '指定すると、この投稿を基点としてより古い投稿を取得します'
@@ -84,20 +84,20 @@ export const meta = {
 		},
 
 		sinceDate: {
-			validator: $.num.optional,
+			validator: $.optional.num,
 		},
 
 		untilDate: {
-			validator: $.num.optional,
+			validator: $.optional.num,
 		},
 
 		offset: {
-			validator: $.num.optional.min(0),
+			validator: $.optional.num.min(0),
 			default: 0
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 30),
+			validator: $.optional.num.range(1, 30),
 			default: 10
 		},
 	}
diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts
index 20bb7f7902..aff3ec8cb6 100644
--- a/src/server/api/endpoints/notes/timeline.ts
+++ b/src/server/api/endpoints/notes/timeline.ts
@@ -18,7 +18,7 @@ export const meta = {
 
 	params: {
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10,
 			desc: {
 				'ja-JP': '最大数'
@@ -26,7 +26,7 @@ export const meta = {
 		},
 
 		sinceId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 			desc: {
 				'ja-JP': '指定すると、この投稿を基点としてより新しい投稿を取得します'
@@ -34,7 +34,7 @@ export const meta = {
 		},
 
 		untilId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 			desc: {
 				'ja-JP': '指定すると、この投稿を基点としてより古い投稿を取得します'
@@ -42,21 +42,21 @@ export const meta = {
 		},
 
 		sinceDate: {
-			validator: $.num.optional,
+			validator: $.optional.num,
 			desc: {
 				'ja-JP': '指定した時間を基点としてより新しい投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
 			}
 		},
 
 		untilDate: {
-			validator: $.num.optional,
+			validator: $.optional.num,
 			desc: {
 				'ja-JP': '指定した時間を基点としてより古い投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
 			}
 		},
 
 		includeMyRenotes: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: true,
 			desc: {
 				'ja-JP': '自分の行ったRenoteを含めるかどうか'
@@ -64,7 +64,7 @@ export const meta = {
 		},
 
 		includeRenotedMyNotes: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: true,
 			desc: {
 				'ja-JP': 'Renoteされた自分の投稿を含めるかどうか'
@@ -72,7 +72,7 @@ export const meta = {
 		},
 
 		includeLocalRenotes: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: true,
 			desc: {
 				'ja-JP': 'Renoteされたローカルの投稿を含めるかどうか'
@@ -80,14 +80,14 @@ export const meta = {
 		},
 
 		withFiles: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します'
 			}
 		},
 
 		mediaOnly: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
 			}
diff --git a/src/server/api/endpoints/notes/user-list-timeline.ts b/src/server/api/endpoints/notes/user-list-timeline.ts
index 279511e834..eced85353d 100644
--- a/src/server/api/endpoints/notes/user-list-timeline.ts
+++ b/src/server/api/endpoints/notes/user-list-timeline.ts
@@ -25,7 +25,7 @@ export const meta = {
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10,
 			desc: {
 				'ja-JP': '最大数'
@@ -33,7 +33,7 @@ export const meta = {
 		},
 
 		sinceId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 			desc: {
 				'ja-JP': '指定すると、この投稿を基点としてより新しい投稿を取得します'
@@ -41,7 +41,7 @@ export const meta = {
 		},
 
 		untilId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 			desc: {
 				'ja-JP': '指定すると、この投稿を基点としてより古い投稿を取得します'
@@ -49,21 +49,21 @@ export const meta = {
 		},
 
 		sinceDate: {
-			validator: $.num.optional,
+			validator: $.optional.num,
 			desc: {
 				'ja-JP': '指定した時間を基点としてより新しい投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
 			}
 		},
 
 		untilDate: {
-			validator: $.num.optional,
+			validator: $.optional.num,
 			desc: {
 				'ja-JP': '指定した時間を基点としてより古い投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
 			}
 		},
 
 		includeMyRenotes: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: true,
 			desc: {
 				'ja-JP': '自分の行ったRenoteを含めるかどうか'
@@ -71,7 +71,7 @@ export const meta = {
 		},
 
 		includeRenotedMyNotes: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: true,
 			desc: {
 				'ja-JP': 'Renoteされた自分の投稿を含めるかどうか'
@@ -79,7 +79,7 @@ export const meta = {
 		},
 
 		includeLocalRenotes: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: true,
 			desc: {
 				'ja-JP': 'Renoteされたローカルの投稿を含めるかどうか'
@@ -87,14 +87,14 @@ export const meta = {
 		},
 
 		withFiles: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します'
 			}
 		},
 
 		mediaOnly: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			desc: {
 				'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
 			}
diff --git a/src/server/api/endpoints/users.ts b/src/server/api/endpoints/users.ts
index aef5bd8507..c0da464831 100644
--- a/src/server/api/endpoints/users.ts
+++ b/src/server/api/endpoints/users.ts
@@ -7,17 +7,17 @@ export const meta = {
 
 	params: {
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		offset: {
-			validator: $.num.optional.min(0),
+			validator: $.optional.num.min(0),
 			default: 0
 		},
 
 		sort: {
-			validator: $.str.optional.or([
+			validator: $.optional.str.or([
 				'+follower',
 				'-follower',
 				'+createdAt',
@@ -28,7 +28,7 @@ export const meta = {
 		},
 
 		origin: {
-			validator: $.str.optional.or([
+			validator: $.optional.str.or([
 				'combined',
 				'local',
 				'remote',
diff --git a/src/server/api/endpoints/users/followers.ts b/src/server/api/endpoints/users/followers.ts
index d04b01b09e..2a39da4064 100644
--- a/src/server/api/endpoints/users/followers.ts
+++ b/src/server/api/endpoints/users/followers.ts
@@ -25,18 +25,18 @@ export const meta = {
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		cursor: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			default: null as any,
 			transform: transform,
 		},
 
 		iknow: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: false,
 		}
 	}
diff --git a/src/server/api/endpoints/users/following.ts b/src/server/api/endpoints/users/following.ts
index bcfa777d66..4ccc13f633 100644
--- a/src/server/api/endpoints/users/following.ts
+++ b/src/server/api/endpoints/users/following.ts
@@ -25,18 +25,18 @@ export const meta = {
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		cursor: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			default: null as any,
 			transform: transform,
 		},
 
 		iknow: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: false,
 		}
 	}
diff --git a/src/server/api/endpoints/users/get_frequently_replied_users.ts b/src/server/api/endpoints/users/get_frequently_replied_users.ts
index 74de7b4589..e897fe8db2 100644
--- a/src/server/api/endpoints/users/get_frequently_replied_users.ts
+++ b/src/server/api/endpoints/users/get_frequently_replied_users.ts
@@ -20,7 +20,7 @@ export const meta = {
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 	}
diff --git a/src/server/api/endpoints/users/notes.ts b/src/server/api/endpoints/users/notes.ts
index c7f63d783e..5c776e48e2 100644
--- a/src/server/api/endpoints/users/notes.ts
+++ b/src/server/api/endpoints/users/notes.ts
@@ -14,7 +14,7 @@ export const meta = {
 
 	params: {
 		userId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 			desc: {
 				'ja-JP': '対象のユーザーのID',
@@ -23,18 +23,18 @@ export const meta = {
 		},
 
 		username: {
-			validator: $.str.optional,
+			validator: $.optional.str,
 			desc: {
 				'ja-JP': 'ユーザー名'
 			}
 		},
 
 		host: {
-			validator: $.str.optional.nullable,
+			validator: $.optional.nullable.str,
 		},
 
 		includeReplies: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: true,
 
 			desc: {
@@ -43,7 +43,7 @@ export const meta = {
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10,
 			desc: {
 				'ja-JP': '最大数'
@@ -51,7 +51,7 @@ export const meta = {
 		},
 
 		sinceId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 			desc: {
 				'ja-JP': '指定すると、この投稿を基点としてより新しい投稿を取得します'
@@ -59,7 +59,7 @@ export const meta = {
 		},
 
 		untilId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 			desc: {
 				'ja-JP': '指定すると、この投稿を基点としてより古い投稿を取得します'
@@ -67,21 +67,21 @@ export const meta = {
 		},
 
 		sinceDate: {
-			validator: $.num.optional,
+			validator: $.optional.num,
 			desc: {
 				'ja-JP': '指定した時間を基点としてより新しい投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
 			}
 		},
 
 		untilDate: {
-			validator: $.num.optional,
+			validator: $.optional.num,
 			desc: {
 				'ja-JP': '指定した時間を基点としてより古い投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
 			}
 		},
 
 		includeMyRenotes: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: true,
 			desc: {
 				'ja-JP': '自分の行ったRenoteを含めるかどうか'
@@ -89,7 +89,7 @@ export const meta = {
 		},
 
 		includeRenotedMyNotes: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: true,
 			desc: {
 				'ja-JP': 'Renoteされた自分の投稿を含めるかどうか'
@@ -97,7 +97,7 @@ export const meta = {
 		},
 
 		includeLocalRenotes: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: true,
 			desc: {
 				'ja-JP': 'Renoteされたローカルの投稿を含めるかどうか'
@@ -105,7 +105,7 @@ export const meta = {
 		},
 
 		withFiles: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: false,
 			desc: {
 				'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します'
@@ -113,7 +113,7 @@ export const meta = {
 		},
 
 		mediaOnly: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: false,
 			desc: {
 				'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
@@ -121,14 +121,14 @@ export const meta = {
 		},
 
 		fileType: {
-			validator: $.arr($.str).optional,
+			validator: $.optional.arr($.str),
 			desc: {
 				'ja-JP': '指定された種類のファイルが添付された投稿のみを取得します'
 			}
 		},
 
 		excludeNsfw: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: false,
 			desc: {
 				'ja-JP': 'true にすると、NSFW指定されたファイルを除外します(fileTypeが指定されている場合のみ有効)'
diff --git a/src/server/api/endpoints/users/recommendation.ts b/src/server/api/endpoints/users/recommendation.ts
index e3a03888b6..e9f0107716 100644
--- a/src/server/api/endpoints/users/recommendation.ts
+++ b/src/server/api/endpoints/users/recommendation.ts
@@ -21,12 +21,12 @@ export const meta = {
 
 	params: {
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10
 		},
 
 		offset: {
-			validator: $.num.optional.min(0),
+			validator: $.optional.num.min(0),
 			default: 0
 		}
 	}
diff --git a/src/server/api/endpoints/users/search.ts b/src/server/api/endpoints/users/search.ts
index b0a789c7d5..5dc1e4aeab 100644
--- a/src/server/api/endpoints/users/search.ts
+++ b/src/server/api/endpoints/users/search.ts
@@ -19,7 +19,7 @@ export const meta = {
 		},
 
 		offset: {
-			validator: $.num.optional.min(0),
+			validator: $.optional.num.min(0),
 			default: 0,
 			desc: {
 				'ja-JP': 'オフセット'
@@ -27,7 +27,7 @@ export const meta = {
 		},
 
 		limit: {
-			validator: $.num.optional.range(1, 100),
+			validator: $.optional.num.range(1, 100),
 			default: 10,
 			desc: {
 				'ja-JP': '取得する数'
@@ -35,7 +35,7 @@ export const meta = {
 		},
 
 		localOnly: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: false,
 			desc: {
 				'ja-JP': 'ローカルユーザーのみ検索対象にするか否か'
@@ -43,7 +43,7 @@ export const meta = {
 		},
 
 		detail: {
-			validator: $.bool.optional,
+			validator: $.optional.bool,
 			default: true,
 			desc: {
 				'ja-JP': '詳細なユーザー情報を含めるか否か'
diff --git a/src/server/api/endpoints/users/show.ts b/src/server/api/endpoints/users/show.ts
index 67548599ba..05d188a6a0 100644
--- a/src/server/api/endpoints/users/show.ts
+++ b/src/server/api/endpoints/users/show.ts
@@ -16,7 +16,7 @@ export const meta = {
 
 	params: {
 		userId: {
-			validator: $.type(ID).optional,
+			validator: $.optional.type(ID),
 			transform: transform,
 			desc: {
 				'ja-JP': '対象のユーザーのID',
@@ -25,7 +25,7 @@ export const meta = {
 		},
 
 		userIds: {
-			validator: $.arr($.type(ID)).optional.unique(),
+			validator: $.optional.arr($.type(ID)).unique(),
 			transform: transformMany,
 			desc: {
 				'ja-JP': 'ユーザーID (配列)'
@@ -33,11 +33,11 @@ export const meta = {
 		},
 
 		username: {
-			validator: $.str.optional
+			validator: $.optional.str
 		},
 
 		host: {
-			validator: $.str.optional.nullable
+			validator: $.optional.nullable.str
 		}
 	}
 };
diff --git a/src/server/web/docs.ts b/src/server/web/docs.ts
index 5777c03e70..c0e03f9146 100644
--- a/src/server/web/docs.ts
+++ b/src/server/web/docs.ts
@@ -9,7 +9,6 @@ import 'showdown-highlightjs-extension';
 import ms = require('ms');
 import * as Router from 'koa-router';
 import * as send from 'koa-send';
-import { Context, ObjectContext } from 'cafy';
 import * as glob from 'glob';
 import * as yaml from 'js-yaml';
 import config from '../../config';
@@ -125,27 +124,6 @@ const sortParams = (params: { name: string }[]) => {
 	return params;
 };
 
-// WIP type
-const extractParamDefRef = (params: Context[]) => {
-	let defs: any[] = [];
-
-	for (const param of params) {
-		if (param.data && param.data.ref) {
-			const props = (param as ObjectContext<any>).props;
-			defs.push({
-				name: param.data.ref,
-				params: sortParams(Object.keys(props).map(k => parseParamDefinition(k, props[k])))
-			});
-
-			const childDefs = extractParamDefRef(Object.keys(props).map(k => props[k]));
-
-			defs = defs.concat(childDefs);
-		}
-	}
-
-	return sortParams(defs);
-};
-
 const extractPropDefRef = (props: any[]) => {
 	let defs: any[] = [];
 
@@ -189,7 +167,6 @@ router.get('/*/api/endpoints/*', async ctx => {
 		},
 		// @ts-ignore
 		params: ep.meta.params ? sortParams(Object.entries(ep.meta.params).map(([k, v]) => parseParamDefinition(k, v))) : null,
-		paramDefs: ep.meta.params ? extractParamDefRef(Object.values(ep.meta.params).map(x => x.validator)) : null,
 		res: ep.meta.res,
 		resProps: ep.meta.res && ep.meta.res.props ? sortParams(Object.entries(ep.meta.res.props).map(([k, v]) => parsePropDefinition(k, v))) : null,
 		resDefs: null as any, //extractPropDefRef(Object.entries(ep.res.props).map(([k, v]) => parsePropDefinition(k, v)))