Repository: discordjs/discord.js
File: packages/builders/src/components/Assertions.ts (TypeScript)

Code before:
		name: z.string().min(2).max(32).optional(),
		animated: z.boolean().optional(),
	})
	.refine((data) => data.id !== undefined || data.name !== undefined, {
		error: "Either 'id' or 'name' must be provided",
	});

const buttonPredicateBase = z.strictObject({
	type: z.literal(ComponentType.Button),
	disabled: z.boolean().optional(),
});

const buttonCustomIdPredicateBase = buttonPredicateBase.extend({
	custom_id: customIdPredicate,
	emoji: emojiPredicate.optional(),
	label: labelPredicate,
});

const buttonPrimaryPredicate = buttonCustomIdPredicateBase.extend({ style: z.literal(ButtonStyle.Primary) });
const buttonSecondaryPredicate = buttonCustomIdPredicateBase.extend({ style: z.literal(ButtonStyle.Secondary) });
const buttonSuccessPredicate = buttonCustomIdPredicateBase.extend({ style: z.literal(ButtonStyle.Success) });
const buttonDangerPredicate = buttonCustomIdPredicateBase.extend({ style: z.literal(ButtonStyle.Danger) });

const buttonLinkPredicate = buttonPredicateBase.extend({
	style: z.literal(ButtonStyle.Link),
	url: z.url({ protocol: /^(?:https?|attachment)$/ }),
	emoji: emojiPredicate.optional(),
	label: labelPredicate,
});

const buttonPremiumPredicate = buttonPredicateBase.extend({
	style: z.literal(ButtonStyle.Premium),
	sku_id: z.string(),
});

export const buttonPredicate = z.discriminatedUnion('style', [
	buttonLinkPredicate,
	buttonPrimaryPredicate,
	buttonSecondaryPredicate,
	buttonSuccessPredicate,
	buttonDangerPredicate,
	buttonPremiumPredicate,
]);

const selectMenuBasePredicate = z.object({
	placeholder: z.string().max(150).optional(),
	min_values: z.number().min(0).max(25).optional(),
	max_values: z.number().min(0).max(25).optional(),
	custom_id: customIdPredicate,
	disabled: z.boolean().optional(),
});

Review comment (suggestion):
```suggestion
	url: z.url({ protocol: /^(?:https?|discord)$/ }),
```

Code after:
		name: z.string().min(2).max(32).optional(),
		animated: z.boolean().optional(),
	})
	.refine((data) => data.id !== undefined || data.name !== undefined, {
		error: "Either 'id' or 'name' must be provided",
	});

const buttonPredicateBase = z.strictObject({
	type: z.literal(ComponentType.Button),
	disabled: z.boolean().optional(),
});

const buttonCustomIdPredicateBase = buttonPredicateBase.extend({
	custom_id: customIdPredicate,
	emoji: emojiPredicate.optional(),
	label: labelPredicate,
});

const buttonPrimaryPredicate = buttonCustomIdPredicateBase.extend({ style: z.literal(ButtonStyle.Primary) });
const buttonSecondaryPredicate = buttonCustomIdPredicateBase.extend({ style: z.literal(ButtonStyle.Secondary) });
const buttonSuccessPredicate = buttonCustomIdPredicateBase.extend({ style: z.literal(ButtonStyle.Success) });
const buttonDangerPredicate = buttonCustomIdPredicateBase.extend({ style: z.literal(ButtonStyle.Danger) });

const buttonLinkPredicate = buttonPredicateBase.extend({
	style: z.literal(ButtonStyle.Link),
	url: z.url({ protocol: /^(?:https?|discord)$/ }),
	emoji: emojiPredicate.optional(),
	label: labelPredicate,
});

const buttonPremiumPredicate = buttonPredicateBase.extend({
	style: z.literal(ButtonStyle.Premium),
	sku_id: z.string(),
});

export const buttonPredicate = z.discriminatedUnion('style', [
	buttonLinkPredicate,
	buttonPrimaryPredicate,
	buttonSecondaryPredicate,
	buttonSuccessPredicate,
	buttonDangerPredicate,
	buttonPremiumPredicate,
]);

const selectMenuBasePredicate = z.object({
	placeholder: z.string().max(150).optional(),
	min_values: z.number().min(0).max(25).optional(),
	max_values: z.number().min(0).max(25).optional(),
	custom_id: customIdPredicate,
	disabled: z.boolean().optional(),
});