diff --git a/packages/backend/src/misc/schema.ts b/packages/backend/src/misc/schema.ts index fdecc278d4..7aeb65f296 100644 --- a/packages/backend/src/misc/schema.ts +++ b/packages/backend/src/misc/schema.ts @@ -132,11 +132,27 @@ type NullOrUndefined<p extends Schema, T> = // https://stackoverflow.com/questions/54938141/typescript-convert-union-to-intersection // Get intersection from union type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never; +type PartialIntersection<T> = Partial<UnionToIntersection<T>>; // https://github.com/misskey-dev/misskey/pull/8144#discussion_r785287552 // To get union, we use `Foo extends any ? Hoge<Foo> : never` type UnionSchemaType<a extends readonly any[], X extends Schema = a[number]> = X extends any ? SchemaType<X> : never; -type ArrayUnion<T> = T extends any ? Array<T> : never; +type UnionObjectSchemaType<a extends readonly any[], X extends Schema = a[number]> = X extends any ? ObjectSchemaType<X> : never; +type ArrayUnion<T> = T extends any ? Array<T> : never; + +type ObjectSchemaTypeDef<p extends Schema> = + p['ref'] extends keyof typeof refs ? Packed<p['ref']> : + p['properties'] extends NonNullable<Obj> ? + p['anyOf'] extends ReadonlyArray<Schema> ? + ObjType<p['properties'], NonNullable<p['required']>[number]> & UnionObjectSchemaType<p['anyOf']> & PartialIntersection<UnionObjectSchemaType<p['anyOf']>> + : + ObjType<p['properties'], NonNullable<p['required']>[number]> + : + p['anyOf'] extends ReadonlyArray<Schema> ? UnionObjectSchemaType<p['anyOf']> & PartialIntersection<UnionObjectSchemaType<p['anyOf']>> : + p['allOf'] extends ReadonlyArray<Schema> ? UnionToIntersection<UnionSchemaType<p['allOf']>> : + any + +type ObjectSchemaType<p extends Schema> = NullOrUndefined<p, ObjectSchemaTypeDef<p>>; export type SchemaTypeDef<p extends Schema> = p['type'] extends 'null' ? null : @@ -149,13 +165,7 @@ export type SchemaTypeDef<p extends Schema> = string ) : p['type'] extends 'boolean' ? boolean : - p['type'] extends 'object' ? ( - p['ref'] extends keyof typeof refs ? Packed<p['ref']> : - p['properties'] extends NonNullable<Obj> ? ObjType<p['properties'], NonNullable<p['required']>[number]> : - p['anyOf'] extends ReadonlyArray<Schema> ? UnionSchemaType<p['anyOf']> & Partial<UnionToIntersection<UnionSchemaType<p['anyOf']>>> : - p['allOf'] extends ReadonlyArray<Schema> ? UnionToIntersection<UnionSchemaType<p['allOf']>> : - any - ) : + p['type'] extends 'object' ? ObjectSchemaTypeDef<p> : p['type'] extends 'array' ? ( p['items'] extends OfSchema ? ( p['items']['anyOf'] extends ReadonlyArray<Schema> ? UnionSchemaType<NonNullable<p['items']['anyOf']>>[] : @@ -166,6 +176,7 @@ export type SchemaTypeDef<p extends Schema> = p['items'] extends NonNullable<Schema> ? SchemaTypeDef<p['items']>[] : any[] ) : + p['anyOf'] extends ReadonlyArray<Schema> ? UnionSchemaType<p['anyOf']> & PartialIntersection<UnionSchemaType<p['anyOf']>> : p['oneOf'] extends ReadonlyArray<Schema> ? UnionSchemaType<p['oneOf']> : any; diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 92bc8a7595..a709ab2f7a 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -90,48 +90,13 @@ export const paramDef = { visibleUserIds: { type: 'array', uniqueItems: true, items: { type: 'string', format: 'misskey:id', } }, - text: { type: 'string', maxLength: MAX_NOTE_TEXT_LENGTH, nullable: true }, cw: { type: 'string', nullable: true, maxLength: 100 }, localOnly: { type: 'boolean', default: false }, noExtractMentions: { type: 'boolean', default: false }, noExtractHashtags: { type: 'boolean', default: false }, noExtractEmojis: { type: 'boolean', default: false }, - fileIds: { - type: 'array', - uniqueItems: true, - minItems: 1, - maxItems: 16, - items: { type: 'string', format: 'misskey:id' }, - }, - mediaIds: { - deprecated: true, - description: 'Use `fileIds` instead. If both are specified, this property is discarded.', - type: 'array', - uniqueItems: true, - minItems: 1, - maxItems: 16, - items: { type: 'string', format: 'misskey:id' }, - }, replyId: { type: 'string', format: 'misskey:id', nullable: true }, - renoteId: { type: 'string', format: 'misskey:id', nullable: true }, channelId: { type: 'string', format: 'misskey:id', nullable: true }, - poll: { - type: 'object', - nullable: true, - properties: { - choices: { - type: 'array', - uniqueItems: true, - minItems: 2, - maxItems: 10, - items: { type: 'string', minLength: 1, maxLength: 50 }, - }, - multiple: { type: 'boolean', default: false }, - expiresAt: { type: 'integer', nullable: true }, - expiredAfter: { type: 'integer', nullable: true, minimum: 1 }, - }, - required: ['choices'], - }, }, anyOf: [ { @@ -143,21 +108,60 @@ export const paramDef = { }, { // (re)note with files, text and poll are optional + properties: { + fileIds: { + type: 'array', + uniqueItems: true, + minItems: 1, + maxItems: 16, + items: { type: 'string', format: 'misskey:id' }, + }, + }, required: ['fileIds'], }, { // (re)note with files, text and poll are optional + properties: { + mediaIds: { + deprecated: true, + description: 'Use `fileIds` instead. If both are specified, this property is discarded.', + type: 'array', + uniqueItems: true, + minItems: 1, + maxItems: 16, + items: { type: 'string', format: 'misskey:id' }, + }, + }, required: ['mediaIds'], }, { // (re)note with poll, text and files are optional properties: { - poll: { type: 'object', nullable: false }, + poll: { + type: 'object', + nullable: true, + properties: { + choices: { + type: 'array', + uniqueItems: true, + minItems: 2, + maxItems: 10, + items: { type: 'string', minLength: 1, maxLength: 50 }, + }, + multiple: { type: 'boolean' }, + expiresAt: { type: 'integer', nullable: true }, + expiredAfter: { type: 'integer', nullable: true, minimum: 1 }, + }, + required: ['choices'], + }, }, required: ['poll'], }, { // pure renote + properties: { + renoteId: { type: 'string', format: 'misskey:id', nullable: true }, + }, required: ['renoteId'], }, ], diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts index 95491211bc..b176e6c65d 100644 --- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts +++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts @@ -29,14 +29,22 @@ export const meta = { export const paramDef = { type: 'object', properties: { - username: { type: 'string', nullable: true }, - host: { type: 'string', nullable: true }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, detail: { type: 'boolean', default: true }, }, anyOf: [ - { required: ['username'] }, - { required: ['host'] }, + { + properties: { + username: { type: 'string', nullable: true }, + }, + required: ['username'] + }, + { + properties: { + host: { type: 'string', nullable: true }, + }, + required: ['host'] + }, ], } as const;