mirror of
				https://codeberg.org/yeentown/barkey.git
				synced 2025-11-04 07:24:13 +00:00 
			
		
		
		
	enhance(backend): enhance SchemaType handling of anyOf (#9762)
* enhance(backend): enhance anyOf handling * clean up
This commit is contained in:
		
							parent
							
								
									387fcd5c5d
								
							
						
					
					
						commit
						7c3143b8e5
					
				
					 3 changed files with 71 additions and 48 deletions
				
			
		| 
						 | 
				
			
			@ -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;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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'],
 | 
			
		||||
		},
 | 
			
		||||
	],
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue