local provider = require("@self/embed/provider")
local field = require("@self/embed/field")
local thumbnail = require("@self/embed/thumbnail")
local embed = require("@self/embed/embed")
local author = require("@self/embed/author")
local image = require("@self/embed/image")
local footer = require("@self/embed/footer")
local video = require("@self/embed/video")
local invite = require("@self/invite")
local emoji = require("@self/emoji")
local channel = require("@self/channel")
local messageParams = require("@self/thread/messageParams")
local messageThread = require("@self/thread/messageThread")
local forumMedia = require("@self/thread/forumMedia")
local noMessageThread = require("@self/thread/noMessageThread")
local guild = require("@self/guild/guild")
local onboardingOption = require("@self/guild/onboarding/option")
local prompt = require("@self/guild/onboarding/prompt")
local onboarding = require("@self/guild/onboarding/onboarding")
local welcomeScreenChannel = require("@self/guild/welcomeScreen/channel")
local welcomeScreen = require("@self/guild/welcomeScreen/welcomeScreen")
local overwrite = require("@self/guild/overwrite")
local widgetSettings = require("@self/guild/widgetSettings")
local role = require("@self/guild/role")
local forumTag = require("@self/forumTag")
local actionRow = require("@self/message/components/actionRow")
local textInput = require("@self/message/components/textInput")
local modal = require("@self/message/components/modal")
local button = require("@self/message/components/button")
local selectMenuOption = require("@self/message/components/selectMenu/option")
local defaultValue = require("@self/message/components/selectMenu/defaultValue")
local selectMenu = require("@self/message/components/selectMenu/selectMenu")
local media = require("@self/message/poll/media")
local answer = require("@self/message/poll/answer")
local poll = require("@self/message/poll/poll")
local message = require("@self/message/message")
local reference = require("@self/message/reference")
local allowedMention = require("@self/message/allowedMention")
local option = require("@self/interaction/option")
local choice = require("@self/interaction/choice")
local interaction = require("@self/interaction/interaction")
local attachment = require("@self/attachment")
local defaultReaction = require("@self/default/reaction")
local permission = require("@self/permission")
local intents = require("@self/intents")
local activity = require("@self/activity")
local presence = require("@self/presence")
local automoderationRule = require("@self/automoderation/rule")
local automoderationAction = require("@self/automoderation/action/action")
local automoderationActionMetadata = require("@self/automoderation/action/metadata")
local automoderationTriggerMetadata = require("@self/automoderation/trigger/metadata")

export type providerJSON = provider.JSON
export type fieldJSON = field.JSON
export type thumbnailJSON = thumbnail.JSON
export type embedJSON = embed.JSON
export type authorJSON = author.JSON
export type imageJSON = image.JSON
export type footerJSON = footer.JSON
export type videoJSON = video.JSON
export type inviteJSON = invite.JSON
export type emojiJSON = emoji.JSON
export type channelJSON = channel.JSON
export type messageParamsJSON = messageParams.JSON
export type messageThreadJSON = messageThread.JSON
export type forumMediaJSON = forumMedia.JSON
export type noMessageThreadJSON = noMessageThread.JSON
export type guildJSON = guild.JSON
export type onboardingOptionJSON = onboardingOption.JSON
export type promptJSON = prompt.JSON
export type onboardingJSON = onboarding.JSON
export type welcomeScreenChannelJSON = welcomeScreenChannel.JSON
export type welcomeScreenJSON = welcomeScreen.JSON
export type overwriteJSON = overwrite.JSON
export type widgetSettingsJSON = widgetSettings.JSON
export type roleJSON = role.JSON
export type forumTagJSON = forumTag.JSON
export type actionRowJSON = actionRow.JSON
export type textInputJSON = textInput.JSON
export type buttonJSON = button.JSON
export type selectMenuOptionJSON = selectMenuOption.JSON
export type defaultValueJSON = defaultValue.JSON
export type selectMenuJSON = selectMenu.JSON
export type mediaJSON = media.JSON
export type answerJSON = answer.JSON
export type pollJSON = poll.JSON
export type messageJSON = message.JSON
export type referenceJSON = reference.JSON
export type allowedMentionJSON = allowedMention.JSON
export type optionJSON = option.JSON
export type choiceJSON = choice.JSON
export type interactionJSON = interaction.JSON
export type attachmentJSON = attachment.JSON
export type defaultReactionJSON = defaultReaction.JSON
export type permissionJSON = permission.JSON
export type activityJSON = activity.JSON
export type presenceJSON = presence.JSON
export type AutomoderationRuleJSON = automoderationRule.JSON
export type AutomoderationActionJSON = automoderationAction.JSON
export type AutomoderationActionMetadataJSON = automoderationActionMetadata.JSON
export type AutomoderationTriggerMetadataJSON = automoderationTriggerMetadata.JSON
export type ModalJSON = modal.JSON
export type intents = intents.Intents

return {
	intents = intents,
	permission = permission,
	defaultReaction = defaultReaction,
	attachment = attachment,
	presence = presence,
	activity = activity,
	automoderation = {
		rule = automoderationRule,
		action = {
			action = automoderationAction,
			metadata = automoderationActionMetadata,
		},
		triggerMetadata = automoderationTriggerMetadata,
	},
	interaction = {
		interaction = interaction,
		choice = choice,
		option = option,
	},
	message = {
		allowedMention = allowedMention,
		reference = reference,
		message = message,
		poll = {
			poll = poll,
			answer = answer,
			media = media,
		},
		components = {
			selectMenu = {
				selectMenu = selectMenu,
				defaultValue = defaultValue,
				option = selectMenuOption,
			},
			modal = modal,
			button = button,
			textInput = textInput,
			actionRow = actionRow,
		},
	},
	forumTag = forumTag,
	guild = {
		role = role,
		widgetSettings = widgetSettings,
		overwrite = overwrite,
		welcomeScreen = {
			welcomeScreen = welcomeScreen,
			channel = welcomeScreenChannel,
		},
		onboarding = {
			onboarding = onboarding,
			prompt = prompt,
			option = onboardingOption,
		},
		guild = guild,
	},
	thread = {
		noMessageThread = noMessageThread,
		forumMedia = forumMedia,
		messageThread = messageThread,
		messageParams = messageParams,
	},
	channel = channel,
	emoji = emoji,
	invite = invite,
	embed = {
		video = video,
		footer = footer,
		image = image,
		author = author,
		embed = embed,
		thumbnail = thumbnail,
		field = field,
		provider = provider,
	},
}


--[=[
	@class Builders.Presence
	@__index Prototype

	Implementation of a Discord Presence as a Luau builder.

	https://discord.com/developers/docs/events/gateway-events#update-presence
]=]

local apiTypes = require("../luau_packages/api_types")

local activity = require("./activity")

local Presence = {}

Presence.Prototype = {}
Presence.Interface = {}

--[=[
	@within Builders.Presence
	@private

	Adds an activity to the presence.
]=]
function Presence.Prototype.addActivity(self: Presence, activity: activity.JSON): Presence
	table.insert(self.activities, activity)

	return self
end

--[=[
	@within Builders.Presence
	@private

	Sets the status of the presence.
]=]
function Presence.Prototype.setStatus(self: Presence, status: apiTypes.Status): Presence
	assert(
		apiTypes.client.Status[status],
		`Expected status to be any of the following: 'Online, DoNotDisturb, Idle, Invisible, Offline'`
	)

	self.status = status

	return self
end

--[=[
	@within Builders.Presence
	@private

	Sets whether the presence is AFK.
]=]
function Presence.Prototype.setAfk(self: Presence, isAfk: boolean): Presence
	self.afk = isAfk

	return self
end

--[=[
	@within Builders.Presence
	@private

	Sets the time since the presence was last updated.
]=]
function Presence.Prototype.setSince(self: Presence, delta: number): Presence
	self.since = delta

	return self
end

--[=[
	@within Builders.Presence
	@private

	Builds the presence object that can be sent to the Discord API.
]=]
function Presence.Prototype.build(self: Presence): JSON
	return {
		since = self.since or 0,
		activities = self.activities,
		afk = self.afk or false,
		status = self.status and apiTypes.client.Status[self.status] or "online",
	}
end

--[=[
	@within Builders.Presence
	@private

	Constructor for the Discord Presence Builder.
]=]
function Presence.Interface.new(resource: {
	status: string?,
	afk: boolean?,
	since: number?,
	activities: { activity.JSON }?,
}?): Presence
	local self = setmetatable(
		{
			--[=[
				@prop activities { Classes.Activity }
				@within Builders.Presence
				An array of activity objects that represent what the client is currently doing.
			]=]
			activities = {},

			--[=[
				@prop since number?
				@within Builders.Presence
				Unix timestamp (in milliseconds) of when the client went idle, or null if the client is not idle.
			]=]

			--[=[
				@prop afk boolean?
				@within Builders.Presence
				Whether or not the client is afk.
			]=]

			--[=[
				@prop status string?
				@within Builders.Presence
				The user's current status. Can be "online", "dnd", "idle", "invisible", or "offline".
			]=]
		} :: Presence,
		{ __index = Presence.Prototype }
	)

	if resource then
		if resource.status then
			self.status = resource.status
		end
		if resource.afk ~= nil then
			self.afk = resource.afk
		end
		if resource.since then
			self.since = resource.since
		end
		if resource.activities then
			self.activities = resource.activities
		end
	end

	return self
end

export type Presence = typeof(Presence.Prototype) & {
	activities: { activity.JSON },
	since: number?,
	afk: boolean?,
	status: string?,
}

export type JSON = typeof(Presence.Prototype.build(nil :: any))

return Presence.Interface


--[=[
	@class Classes.ApplicationBitflag
	@__index Prototype

	Implementation of the Discord Application Bitflags in Luau.
	This class provides methods to check various application flags that indicate different states and capabilities of a Discord application.

	https://discord.com/developers/docs/resources/application#application-object-application-flags
]=]

local bit = require("../../luau_packages/bit")

local ApplicationBitflag = {}

ApplicationBitflag.Interface = {}
ApplicationBitflag.Prototype = {}

--[=[
	@within Classes.ApplicationBitflag
	Indicates if an app uses the Auto Moderation API
]=]
function ApplicationBitflag.Prototype.applicationAutoModerationRoleCreateBadge(self: ApplicationBitflag)
	local targetBit = bit.lshift(1, 6)

	return bit.band(self.flag, targetBit) == targetBit
end

--[=[
	@within Classes.ApplicationBitflag
	Intent required for bots in 100 or more servers to receive presence_update events
]=]
function ApplicationBitflag.Prototype.gatewayPresence(self: ApplicationBitflag)
	local targetBit = bit.lshift(1, 12)

	return bit.band(self.flag, targetBit) == targetBit
end

--[=[
	@within Classes.ApplicationBitflag
	Intent required for bots in under 100 servers to receive presence_update events,
	found on the Bot page in your app's settings
]=]
function ApplicationBitflag.Prototype.gatewayPresenceLimited(self: ApplicationBitflag)
	local targetBit = bit.lshift(1, 13)

	return bit.band(self.flag, targetBit) == targetBit
end

--[=[
	@within Classes.ApplicationBitflag
	Intent required for bots in 100 or more servers to receive member-related events like
	guild_member_add. See the list of member-related events under GUILD_MEMBERS
]=]
function ApplicationBitflag.Prototype.gatewayGuildMembers(self: ApplicationBitflag)
	local targetBit = bit.lshift(1, 14)

	return bit.band(self.flag, targetBit) == targetBit
end

--[=[
	@within Classes.ApplicationBitflag
	Intent required for bots in under 100 servers to receive member-related events
	like guild_member_add, found on the Bot page in your app's settings.

	See the list of member-related events under GUILD_MEMBERS
]=]
function ApplicationBitflag.Prototype.gatewayGuildMembersLimited(self: ApplicationBitflag)
	local targetBit = bit.lshift(1, 15)

	return bit.band(self.flag, targetBit) == targetBit
end

--[=[
	@within Classes.ApplicationBitflag
	Indicates unusual growth of an app that prevents verification
]=]
function ApplicationBitflag.Prototype.verificationPendingGuildLimit(self: ApplicationBitflag)
	local targetBit = bit.lshift(1, 16)

	return bit.band(self.flag, targetBit) == targetBit
end

--[=[
	@within Classes.ApplicationBitflag
	Indicates if an app is embedded within the Discord client (currently unavailable publicly)
]=]
function ApplicationBitflag.Prototype.embedded(self: ApplicationBitflag)
	local targetBit = bit.lshift(1, 17)

	return bit.band(self.flag, targetBit) == targetBit
end

--[=[
	@within Classes.ApplicationBitflag
	Intent required for bots in 100 or more servers to receive message content
]=]
function ApplicationBitflag.Prototype.gatewayMessageContent(self: ApplicationBitflag)
	local targetBit = bit.lshift(1, 18)

	return bit.band(self.flag, targetBit) == targetBit
end

--[=[
	@within Classes.ApplicationBitflag
	Intent required for bots in under 100 servers to receive message content,
	found on the Bot page in your app's settings
]=]
function ApplicationBitflag.Prototype.gatewayMessageContentLimited(self: ApplicationBitflag)
	local targetBit = bit.lshift(1, 19)

	return bit.band(self.flag, targetBit) == targetBit
end

--[=[
	@within Classes.ApplicationBitflag
	Indicates if an app has registered global application commands
]=]
function ApplicationBitflag.Prototype.applicationCommandBadge(self: ApplicationBitflag)
	local targetBit = bit.lshift(1, 23)

	return bit.band(self.flag, targetBit) == targetBit
end

--[=[
	@within Classes.ApplicationBitflag
	@private

	Creates a new ApplicationBitflag instance with the specified flag value.
]=]
function ApplicationBitflag.Interface.new(flag: number): ApplicationBitflag
	local self = setmetatable(
		{
			flag = flag,
		} :: ApplicationBitflag,
		{ __index = ApplicationBitflag.Prototype }
	)

	return self
end

export type ApplicationBitflag = typeof(ApplicationBitflag.Prototype) & {
	flag: number,
}

return ApplicationBitflag.Interface


-- selene: allow(unused_variable)
local state = require("../../luau_packages/state")
local apiTypes = require("../../luau_packages/api_types")

local dmChannel = require("./types/dm")
local groupDmChannel = require("./types/groupDm")
local guildAnnouncement = require("./types/guildAnnouncement")
local guildCategory = require("./types/guildCategory")
local guildDirectory = require("./types/guildDirectory")
local guildStageVoice = require("./types/guildStageVoice")
local guildText = require("./types/guildText")
local guildVoice = require("./types/guildVoice")

local announcementThread = require("./threads/announcement")
local forumThread = require("./threads/forum")
local mediaThread = require("./threads/media")
local privateThread = require("./threads/private")
local publicThread = require("./threads/public")

return function<T>(state: state.State, channelData: apiTypes.ChannelObject): T?
	local object: any

	if channelData.type == apiTypes.channel.ChannelTypes.GuildText then
		object = guildText.new(state, channelData)
	end

	if channelData.type == apiTypes.channel.ChannelTypes.DM then
		object = dmChannel.new(state, channelData)
	end

	if channelData.type == apiTypes.channel.ChannelTypes.GuildVoice then
		object = guildVoice.new(state, channelData)
	end

	if channelData.type == apiTypes.channel.ChannelTypes.GroupDM then
		object = groupDmChannel.new(state, channelData)
	end

	if channelData.type == apiTypes.channel.ChannelTypes.GuildCategory then
		object = guildCategory.new(state, channelData)
	end

	if channelData.type == apiTypes.channel.ChannelTypes.GuildAnnouncement then
		object = guildAnnouncement.new(state, channelData)
	end

	if channelData.type == apiTypes.channel.ChannelTypes.AnnouncementThread then
		object = announcementThread.new(state, channelData)
	end

	if channelData.type == apiTypes.channel.ChannelTypes.PublicThread then
		object = publicThread.new(state, channelData)
	end

	if channelData.type == apiTypes.channel.ChannelTypes.PrivateThread then
		object = privateThread.new(state, channelData)
	end

	if channelData.type == apiTypes.channel.ChannelTypes.GuildStageVoice then
		object = guildStageVoice.new(state, channelData)
	end

	if channelData.type == apiTypes.channel.ChannelTypes.GuildDirectory then
		object = guildDirectory.new(state, channelData)
	end

	if channelData.type == apiTypes.channel.ChannelTypes.GuildForum then
		object = forumThread.new(state, channelData)
	end

	if channelData.type == apiTypes.channel.ChannelTypes.GuildMedia then
		object = mediaThread.new(state, channelData)
	end

	return object :: T
end


local provider = require("@self/embed/provider")
local field = require("@self/embed/field")
local thumbnail = require("@self/embed/thumbnail")
local embed = require("@self/embed/embed")
local author = require("@self/embed/author")
local image = require("@self/embed/image")
local footer = require("@self/embed/footer")
local video = require("@self/embed/video")
local emoji = require("@self/emoji")
local bitflagsGuildMember = require("@self/bitflags/guildMember")
local bitflagsChannel = require("@self/bitflags/channel")
local bitflagsApplication = require("@self/bitflags/application")
local bitflagsMessage = require("@self/bitflags/message")
local bitflagsSystemChannel = require("@self/bitflags/systemChannel")
local bitflagsThreadMember = require("@self/bitflags/threadMember")
local bitflagsAttachment = require("@self/bitflags/attachment")
local bitflagsRole = require("@self/bitflags/role")
local sticker = require("@self/sticker")
local reaction = require("@self/reaction/reaction")
local countDetails = require("@self/reaction/countDetails")
local activityActivity = require("@self/activity/activity")
local activityParty = require("@self/activity/activityParty")
local activitySecrets = require("@self/activity/activitySecrets")
local activityAssets = require("@self/activity/activityAssets")
local activityButton = require("@self/activity/activityButton")
local activityEmoji = require("@self/activity/activityEmoji")
local activityTimestamp = require("@self/activity/activityTimestamp")
local threadMetadata = require("@self/channels/threadMetadata")
local private = require("@self/channels/threads/private")
local threadForum = require("@self/channels/threads/forum")
local threadMedia = require("@self/channels/threads/media")
local public = require("@self/channels/threads/public")
local announcement = require("@self/channels/threads/announcement")
local forumTag = require("@self/channels/forumTag")
local guildAnnouncement = require("@self/channels/types/guildAnnouncement")
local groupDm = require("@self/channels/types/groupDm")
local dm = require("@self/channels/types/dm")
local guildText = require("@self/channels/types/guildText")
local guildStageVoice = require("@self/channels/types/guildStageVoice")
local guildDirectory = require("@self/channels/types/guildDirectory")
local guildCategory = require("@self/channels/types/guildCategory")
local guildVoice = require("@self/channels/types/guildVoice")
local overwrite = require("@self/channels/overwrite")
local behaviourGuild = require("@self/channels/behaviour/guild")
local forum = require("@self/channels/behaviour/forum")
local thread = require("@self/channels/behaviour/thread")
local channel = require("@self/channels/behaviour/channel")
local text = require("@self/channels/behaviour/text")
local groupDM = require("@self/channels/behaviour/groupDM")
local media = require("@self/channels/behaviour/media")
local voice = require("@self/channels/behaviour/voice")
local defaultReaction = require("@self/channels/defaultReaction")
local construct = require("@self/channels/construct")
local mention = require("@self/channels/mention")
local avatarDecoration = require("@self/avatarDecoration")
local user = require("@self/user")
local entitlement = require("@self/entitlement")
local guild = require("@self/guild/guild")
local voiceRegion = require("@self/guild/voiceRegion")
local preview = require("@self/guild/preview")
local invite = require("@self/guild/invite")
local roleTag = require("@self/guild/roleTag")
local unavailableGuild = require("@self/guild/unavailableGuild")
local widgetSettings = require("@self/guild/widget/widgetSettings")
local widget = require("@self/guild/widget/widget")
local welcomeScreen = require("@self/guild/welcomeScreen")
local scheduledEvent = require("@self/guild/scheduledEvent")
local onboardingOption = require("@self/guild/onboarding/option")
local prompt = require("@self/guild/onboarding/prompt")
local onboarding = require("@self/guild/onboarding/onboarding")
local welcomeScreenChannel = require("@self/guild/welcomeScreenChannel")
local ban = require("@self/guild/ban")
local vanityUrl = require("@self/guild/vanityUrl")
local role = require("@self/guild/role")
local guildMember = require("@self/guild/member")
local integration = require("@self/guild/integration/integration")
local integrationApplication = require("@self/guild/integration/application")
local account = require("@self/guild/integration/account")
local roleSubscriptionData = require("@self/message/roleSubscriptionData")
local activity = require("@self/message/activity")
local actionRow = require("@self/message/components/actionRow")
local textInput = require("@self/message/components/textInput")
local button = require("@self/message/components/button")
local selectMenuOption = require("@self/message/components/selectMenu/option")
local defaultValue = require("@self/message/components/selectMenu/defaultValue")
local selectMenu = require("@self/message/components/selectMenu/selectMenu")
local mediaObject = require("@self/message/poll/mediaObject")
local answerCount = require("@self/message/poll/answerCount")
local answer = require("@self/message/poll/answer")
local results = require("@self/message/poll/results")
local poll = require("@self/message/poll/poll")
local message = require("@self/message/message")
local stickerItem = require("@self/message/stickerItem")
local reference = require("@self/message/reference")
local ineractionMetadata = require("@self/message/ineractionMetadata")
local call = require("@self/message/call")
local messageInteraction = require("@self/message/interaction")
local threadMember = require("@self/threadMember")
local presence = require("@self/presence")
local resolved = require("@self/resolved")
local dataOption = require("@self/interaction/dataOption")
local ping = require("@self/interaction/types/ping")
local typesCommand = require("@self/interaction/types/command")
local autocomplete = require("@self/interaction/types/autocomplete")
local modal = require("@self/interaction/types/modal")
local component = require("@self/interaction/types/component")
local data = require("@self/interaction/data")
local interaction = require("@self/interaction/behaviour/interaction")
local attachment = require("@self/attachment")
local application = require("@self/application/application")
local option = require("@self/application/command/option")
local command = require("@self/application/command/command")
local choice = require("@self/application/command/choice")
local installParams = require("@self/application/installParams")
local team = require("@self/application/team/team")
local member = require("@self/application/team/member")
local permission = require("@self/permission")
local automoderationRule = require("@self/automoderation/rule")
local automoderationAction = require("@self/automoderation/action/action")
local automoderationActionMetadata = require("@self/automoderation/action/metadata")
local automoderationTriggerMetadata = require("@self/automoderation/trigger/metadata")

export type Provider = provider.Provider
export type Permission = permission.Permission
export type Application = application.Application
export type Team = team.Team
export type TeamMember = member.Member
export type InstallParams = installParams.InstallParams
export type CommandChoice = choice.Choice
export type Command = command.Command
export type CommandOption = option.Option
export type Attachment = attachment.Attachment
export type Interaction = interaction.Interaction
export type InteractionData = data.Data
export type Component = component.Component
export type Modal = modal.Modal
export type Autocomplete = autocomplete.Autocomplete
export type TypesCommand = typesCommand.Command
export type Ping = ping.Ping
export type DataOption<T> = dataOption.DataOption<T>
export type Resolved = resolved.Resolved
export type Presence = presence.Presence
export type ThreadMember = threadMember.ThreadMember
export type MessageInteraction = messageInteraction.Interaction
export type Call = call.Call
export type InteractionMetadata = ineractionMetadata.IneractionMetadata
export type Reference = reference.Reference
export type StickerItem = stickerItem.StickerItem
export type Message = message.Message
export type Poll = poll.Poll
export type PollResults = results.Results
export type PollAnswer = answer.Answer
export type AnswerCount = answerCount.AnswerCount
export type MediaObject = mediaObject.MediaObject
export type SelectMenu = selectMenu.SelectMenu
export type DefaultValue = defaultValue.DefaultValue
export type SelectMenuOption = selectMenuOption.Option
export type Button = button.Button
export type TextInput = textInput.TextInput
export type ActionRow = actionRow.ActionRow
export type Activity = activity.Activity
export type RoleSubscriptionData = roleSubscriptionData.RoleSubscriptionData
export type Account = account.Account
export type IntegrationApplication = integrationApplication.Application
export type Integration = integration.Integration
export type GuildMember = guildMember.Member
export type Role = role.Role
export type VanityUrl = vanityUrl.VanityUrl
export type Ban = ban.Ban
export type WelcomeScreenChannel = welcomeScreenChannel.WelcomeScreenChannel
export type Onboarding = onboarding.Onboarding
export type Prompt = prompt.Prompt
export type OnboardingOption = onboardingOption.Option
export type ScheduledEvent = scheduledEvent.ScheduledEvent
export type WelcomeScreen = welcomeScreen.WelcomeScreen
export type Widget = widget.Widget
export type WidgetSettings = widgetSettings.WidgetSettings
export type UnavailableGuild = unavailableGuild.UnavailableGuild
export type RoleTag = roleTag.RoleTag
export type Invite = invite.Invite
export type Preview = preview.Preview
export type VoiceRegion = voiceRegion.VoiceRegion
export type Guild = guild.Guild
export type Entitlement = entitlement.Entitlement
export type User = user.User
export type AvatarDecoration = avatarDecoration.AvatarDecoration
export type Mention = mention.ChannelMention
export type DefaultReaction = defaultReaction.DefaultReaction
export type Voice = voice.VoiceBehaviour
export type Media = media.MediaBehaviour
export type GroupDM = groupDM.GroupDMBehaviour
export type Text = text.TextBehaviour
export type Channel = channel.ChannelBehaviour
export type Thread = thread.ThreadBehaviour
export type Forum = forum.ForumBehaviour
export type BehaviourGuild = behaviourGuild.GuildBehaviour
export type Overwrite = overwrite.Overwrite
export type GuildVoice = guildVoice.GuildVoice
export type GuildCategory = guildCategory.GuildCategory
export type GuildDirectory = guildDirectory.GuildDirectory
export type GuildStageVoice = guildStageVoice.GuildStageVoice
export type GuildText = guildText.GuildText
export type DM = dm.DM
export type GroupDm = groupDm.GroupDM
export type GuildAnnouncement = guildAnnouncement.GuildAnnouncement
export type ForumTag = forumTag.ForumTag
export type Announcement = announcement.Announcement
export type Public = public.Public
export type ThreadMedia = threadMedia.Media
export type ThreadForum = threadForum.Forum
export type Private = private.Private
export type ThreadMetadata = threadMetadata.ThreadMetadata
export type ActivityTimestamp = activityTimestamp.ActivityTimestamp
export type ActivityEmoji = activityEmoji.ActivityEmoji
export type ActivityButton = activityButton.ActivityButton
export type ActivityAssets = activityAssets.ActivityAssets
export type ActivitySecrets = activitySecrets.ActivitySecrets
export type ActivityParty = activityParty.ActivityParty
export type ActivityActivity = activityActivity.Activity
export type CountDetails = countDetails.CountDetails
export type Reaction = reaction.Reaction
export type Sticker = sticker.Sticker
export type BitflagsRole = bitflagsRole.RoleBitflag
export type BitflagsAttachment = bitflagsAttachment.AttachmentBitflag
export type BitflagsThreadMember = bitflagsThreadMember.ThreadMemberBitflag
export type BitflagsSystemChannel = bitflagsSystemChannel.SystemChannelBitflag
export type BitflagsMessage = bitflagsMessage.MessageBitflag
export type BitflagsApplication = bitflagsApplication.ApplicationBitflag
export type BitflagsChannel = bitflagsChannel.ChannelBitflag
export type BitflagsGuildMember = bitflagsGuildMember.GuildMemberBitflag
export type Emoji = emoji.Emoji
export type Video = video.Video
export type Footer = footer.Footer
export type Image = image.Image
export type Author = author.Author
export type Embed = embed.Embed
export type Thumbnail = thumbnail.Thumbnail
export type Field = field.Field
export type AutomoderationRule = automoderationRule.AutomoderationRule
export type AutomoderationAction = automoderationAction.Action
export type AutomoderationActionMetadata = automoderationActionMetadata.ActionMetadata
export type AutomoderationTriggerMetadata = automoderationTriggerMetadata.TriggerMetadata

return {
	permission = permission,
	automoderation = {
		rule = automoderationRule,
		action = {
			action = automoderationAction,
			metadata = automoderationActionMetadata,
		},
		triggerMetadata = automoderationTriggerMetadata,
	},
	application = {
		team = {
			member = member,
			team = team,
		},
		installParams = installParams,
		command = {
			choice = choice,
			command = command,
			option = option,
		},
		application = application,
	},
	attachment = attachment,
	interaction = {
		behaviour = {
			interaction = interaction,
		},
		data = data,
		types = {
			component = component,
			modal = modal,
			autocomplete = autocomplete,
			command = typesCommand,
			ping = ping,
		},
		dataOption = dataOption,
	},
	resolved = resolved,
	presence = presence,
	threadMember = threadMember,
	message = {
		interaction = messageInteraction,
		call = call,
		ineractionMetadata = ineractionMetadata,
		reference = reference,
		stickerItem = stickerItem,
		message = message,
		poll = {
			poll = poll,
			results = results,
			answer = answer,
			answerCount = answerCount,
			mediaObject = mediaObject,
		},
		components = {
			selectMenu = {
				selectMenu = selectMenu,
				defaultValue = defaultValue,
				option = selectMenuOption,
			},
			button = button,
			textInput = textInput,
			actionRow = actionRow,
		},
		activity = activityActivity,
		roleSubscriptionData = roleSubscriptionData,
	},
	guild = {
		integration = {
			account = account,
			application = integrationApplication,
			integration = integration,
		},
		member = guildMember,
		role = role,
		vanityUrl = vanityUrl,
		ban = ban,
		welcomeScreenChannel = welcomeScreenChannel,
		onboarding = {
			onboarding = onboarding,
			prompt = prompt,
			option = onboardingOption,
		},
		scheduledEvent = scheduledEvent,
		welcomeScreen = welcomeScreen,
		widget = {
			widget = widget,
			widgetSettings = widgetSettings,
		},
		unavailableGuild = unavailableGuild,
		roleTag = roleTag,
		invite = invite,
		preview = preview,
		voiceRegion = voiceRegion,
		guild = guild,
	},
	entitlement = entitlement,
	user = user,
	avatarDecoration = avatarDecoration,
	channels = {
		mention = mention,
		construct = construct,
		defaultReaction = defaultReaction,
		behaviour = {
			voice = voice,
			media = media,
			groupDM = groupDM,
			text = text,
			channel = channel,
			thread = thread,
			forum = forum,
			guild = behaviourGuild,
		},
		overwrite = overwrite,
		types = {
			guildVoice = guildVoice,
			guildCategory = guildCategory,
			guildDirectory = guildDirectory,
			guildStageVoice = guildStageVoice,
			guildText = guildText,
			dm = dm,
			groupDm = groupDm,
			guildAnnouncement = guildAnnouncement,
		},
		forumTag = forumTag,
		threads = {
			announcement = announcement,
			public = public,
			media = threadMedia,
			forum = threadForum,
			private = private,
		},
		threadMetadata = threadMetadata,
	},
	activity = {
		activityTimestamp = activityTimestamp,
		activityEmoji = activityEmoji,
		activityButton = activityButton,
		activityAssets = activityAssets,
		activitySecrets = activitySecrets,
		activityParty = activityParty,
		activity = activity,
	},
	reaction = {
		countDetails = countDetails,
		reaction = reaction,
	},
	sticker = sticker,
	bitflags = {
		role = bitflagsRole,
		attachment = bitflagsAttachment,
		threadMember = bitflagsThreadMember,
		systemChannel = bitflagsSystemChannel,
		message = bitflagsMessage,
		application = bitflagsApplication,
		channel = bitflagsChannel,
		guildMember = bitflagsGuildMember,
	},
	emoji = emoji,
	embed = {
		video = video,
		footer = footer,
		image = image,
		author = author,
		embed = embed,
		thumbnail = thumbnail,
		field = field,
		provider = provider,
	},
}


local bot = require("@self/bot")

--[=[
	@class DiscordLuau

	Top level documentation for the Discord-Luau library, welcome! 👋

	This package will represent the entrypoint to your adventure using Discord-Luau, through this package you'll generate
	a discord Bot object, this bot Object enables you to write discord bots for the Discord API.

	```luau
	local DiscordLuau = require("/path/to/discord-luau") -- this module!

	-- do something with DiscordLuau
	```
]=]
local DiscordLuau = {}

--[=[
	@prop bot DiscordLuau.bot
	@within DiscordLuau
]=]
DiscordLuau.bot = bot
export type Bot = bot.Bot

return DiscordLuau


local discordLuau = require("@self/../luau_packages/discord")
local discordBuilders = require("@self/../luau_packages/builders")

local env = require("@self/../../../.env")

local discordBot = discordLuau.bot.new({
	token = env.DISCORD_BOT_TOKEN,
	intents = discordBuilders.intents.new({ "Guilds", "GuildMessages" }):build(),
	reconnect = true,
	logLevel = "Info",
})

discordBot.onAllShardsReady:listenOnce(function()
	print("We up!")
end)

discordBot:connectAsync():poll()


local applicationRoleConnectionMetadata = require("@self/applicationRoleConnectionMetadata")
local invite = require("@self/invite")
local gateway = require("@self/gateway")
local emoji = require("@self/emoji")
local stageInstance = require("@self/stageInstance")
local application = require("@self/application")
local request = require("@self/request")
local autoModeration = require("@self/autoModeration")
local auditLog = require("@self/auditLog")
local voice = require("@self/voice")
local poll = require("@self/poll")

local guild = require("@self/guild")
local webhook = require("@self/webhook")
local commands = require("@self/commands")
local sticker = require("@self/sticker")
local guildTemplate = require("@self/guildTemplate")
local user = require("@self/user")
local channel = require("@self/channel")
local guildScheduledEvent = require("@self/guildScheduledEvent")
local message = require("@self/message")
local interaction = require("@self/interaction")

export type Request = request.Request

--[=[
	@class Rest

	Interface for the discord-luau Rest package

	Rest represents all possible Discord HTTP APIs, the goal for this package is to simplify the process of making a
	request to the discord API.
]=]

return {
	--[=[
		@prop interaction Rest.interaction
		@within Rest
	]=]
	interaction = interaction,

	--[=[
		@prop poll Rest.poll
		@within Rest
	]=]
	poll = poll,

	--[=[
		@prop voice Rest.voice
		@within Rest
	]=]
	voice = voice,

	--[=[
		@prop auditLog Rest.auditLog
		@within Rest
	]=]
	auditLog = auditLog,

	--[=[
		@prop autoModeration Rest.autoModeration
		@within Rest
	]=]
	autoModeration = autoModeration,

	--[=[
		@prop message Rest.message
		@within Rest
	]=]
	message = message,

	--[=[
		@prop request Rest.Request
		@within Rest
	]=]
	request = request,

	--[=[
		@prop guildScheduledEvent Rest.guildScheduledEvent
		@within Rest
	]=]
	guildScheduledEvent = guildScheduledEvent,

	--[=[
		@prop application Rest.application
		@within Rest
	]=]
	application = application,

	--[=[
		@prop channel Rest.channel
		@within Rest
	]=]
	channel = channel,

	--[=[
		@prop user Rest.user
		@within Rest
	]=]
	user = user,

	--[=[
		@prop guildTemplate Rest.guildTemplate
		@within Rest
	]=]
	guildTemplate = guildTemplate,

	--[=[
		@prop sticker Rest.sticker
		@within Rest
	]=]
	sticker = sticker,

	--[=[
		@prop commands Rest.commands
		@within Rest
	]=]
	commands = commands,

	--[=[
		@prop stageInstance Rest.stageInstance
		@within Rest
	]=]
	stageInstance = stageInstance,

	--[=[
		@prop webhook Rest.webhook
		@within Rest
	]=]
	webhook = webhook,

	--[=[
		@prop emoji Rest.emoji
		@within Rest
	]=]
	emoji = emoji,

	--[=[
		@prop gateway Rest.gateway
		@within Rest
	]=]
	gateway = gateway,

	--[=[
		@prop invite Rest.invite
		@within Rest
	]=]
	invite = invite,

	--[=[
		@prop applicationRoleConnectionMetadata Rest.applicationRoleConnectionMetadata
		@within Rest
	]=]
	applicationRoleConnectionMetadata = applicationRoleConnectionMetadata,

	--[=[
		@prop guild Rest.guild
		@within Rest
	]=]
	guild = guild,
}


--[=[
	@class Secret
	@__index Prototype

	Implementation of a basic secret object in Luau, primarily used to store bot tokens so that
	they can't be accidentally leaked into the console through a `print`

	```luau
	local a

	a = secret.new("SOMETHING_SECRET")
	print(a) --> "Secret(SOMETHIN--------)"

	a = secret.new(12222)
	print(a) --> "Secret(number)"
	```
]=]

local Secret = {}

Secret.Interface = {}
Secret.Prototype = {}

--[=[
	@within Secret

	Creates a new Secret
]=]
function Secret.Interface.new<T>(value: T): Secret<T>
	local self = setmetatable(
		{
			value = value,
		} :: Secret<T>,
		{
			__concat = function(_self: Secret<T>, otherValue: string)
				assert(typeof(_self.value) == "string", "Cannot concat a Secret with a non-string value.")

				return _self.value .. otherValue
			end,
			__eq = function(_self: Secret<T>, otherValue: string)
				return _self.value == otherValue
			end,
			__len = function(_self: Secret<T>)
				assert(
					typeof(_self.value) == "table" or typeof(_self.value) == "string",
					"Cannot length a Secret with a non-table/non-string value."
				)

				return #_self.value
			end,

			__index = Secret.Prototype,
			__metatabble = "The metatable is locked.",
			__newindex = function()
				error(`Cannot modify a Secret.`)
			end,
			__tostring = function(_self: Secret<T>)
				local typeOfValue = typeof(_self.value)

				if typeOfValue == "string" then
					local value = (_self.value :: any) :: string
					local sizeTohide = #value / 2

					return `Secret({string.sub(value, 1, #value - sizeTohide)}{string.rep("-", sizeTohide)})`
				else
					return `Secret({typeOfValue})`
				end
			end,
		}
	)

	return self
end

export type Secret<T> = typeof(Secret.Prototype) & {
	value: T,
}

return Secret.Interface


--[=[
	@class Snowflake
	@__index Prototype

	Wrapper around Discord snowflakes, allwowing developers to query information about a snowflake
]=]

local stdPolyfills = require("../lune_packages/std_polyfills")

local bit = require("../luau_packages/bit")

local DISCORD_EPOCH = 1420070400000

local Snowflake = {}

Snowflake.Interface = {}
Snowflake.Prototype = {}

--[=[
	@within Snowflake

	Returns the timestamp of the snowflake
]=]
function Snowflake.Prototype.getTimestamp(self: Snowflake)
	local snowflake = tonumber(self.snowflake) :: number
	local timestamp = bit.shift(snowflake, 22) + DISCORD_EPOCH

	return stdPolyfills.datetime.fromUnixTimestamp(timestamp / 1000)
end

--[=[
	@within Snowflake

	Returns the internal worker ID of the snowflake
]=]
function Snowflake.Prototype.getInternalWorkerId(self: Snowflake)
	local snowflake = tonumber(self.snowflake) :: number
	local internalWorkerId = bit.shift(bit.band(snowflake, 0x3E0000), 17)

	return internalWorkerId
end

--[=[
	@within Snowflake

	Returns the internal process ID of the snowflake
]=]
function Snowflake.Prototype.getInternalProcessId(self: Snowflake)
	local snowflake = tonumber(self.snowflake) :: number
	local internalProcessId = bit.shift(bit.band(snowflake, 0x1F0000), 12)

	return internalProcessId
end

--[=[
	@within Snowflake

	Returns the increment of the snowflake
]=]
function Snowflake.Prototype.getIncrement(self: Snowflake)
	local snowflake = tonumber(self.snowflake) :: number
	local internalProcessId = bit.band(snowflake, 0xFFF)

	return internalProcessId
end

--[=[
	@within Snowflake

	Creates a new snowflake
]=]
function Snowflake.Interface.new(snowflake: string)
	return setmetatable(
		{
			snowflake = snowflake,
		} :: Snowflake,
		{ __index = Snowflake.Prototype }
	)
end

export type Snowflake = typeof(Snowflake.Prototype) & {
	snowflake: string,
}

return Snowflake.Interface


--[=[
	@class StdPolyfills.datetime
]=]

local runtime = require("./runtime")

local datetime = {}

--[=[
	@within StdPolyfills.datetime

	Creates a new DateTime from an ISO 8601 date-time string.
]=]
function datetime.fromIsoDate(isoDate: string): DateTime
	local target = runtime.assertRuntime()

	if target == "Lune" then
		return require("./lune/datetime").fromIsoDate(isoDate) :: any
	end

	error(`Out of reach`)
end

--[=[
	@within StdPolyfills.datetime

	Creates a new DateTime from the given UNIX timestamp.

	This timestamp may contain both a whole and fractional part - where the fractional part denotes milliseconds / nanoseconds.
]=]
function datetime.fromUnixTimestamp(unixTimestamp: number): DateTime
	local target = runtime.assertRuntime()

	if target == "Lune" then
		return require("./lune/datetime").fromUnixTimestamp(unixTimestamp) :: any
	end

	error(`Out of reach`)
end

--[=[
	@within StdPolyfills.datetime

	Returns a DateTime representing the current moment in time.
]=]
function datetime.now(): DateTime
	local target = runtime.assertRuntime()

	if target == "Lune" then
		return require("./lune/datetime").now() :: any
	end

	error(`Out of reach`)
end

export type DateTime = typeof(datetime) & {
	unixTimestamp: number,
	unixTimestampMillis: number,

	toIsoDate: (self: DateTime) -> string,
}

return datetime


local net = setmetatable({}, { __index = require("@lune/net") })

net.socket = function(...)
	local socket = require("@lune/net").socket(...)
	local value = setmetatable({}, {
		__index = socket,
	})

	function value.next(...: any)
		return socket:next(...)
	end

	function value.close(...: any)
		return socket:close(...)
	end

	function value.send(...: any)
		return socket:send(...)
	end

	return value
end

return net


--[=[
	@class StdPolyfills.process
]=]

local runtime = require("./runtime")

local process = {}

--[=[
	@within StdPolyfills.process

	Returns the current OS thats being used
]=]
function process.getPlatform(): OS
	local target = runtime.assertRuntime()

	if target == "Lune" then
		return require("./lune/process").os :: OS
	end

	error(`Out of reach`)
end

export type OS = "linux" | "macos" | "windows"

return process


--[=[
	@class StdPolyfills.runtime
]=]

local LUNE_STRING_MATCH = "(Lune) (%d+%.%d+%.%d+.*)+(%d+)"
local ZUNE_STRING_MATCH = "(Zune) (%d+%.%d+%.%d+.*)+(%d+%.%d+)"

local runtime = {}

--[=[
	@within StdPolyfills.runtime

	Will return the Runtime that this code is being executed under.
]=]
function runtime.getRuntime(): RuntimeValue
	local version = _VERSION

	if version == "Luau" then
		-- selene: allow(undefined_variable)
		if game and workspace then
			return "Roblox"
		else
			return "Luau"
		end
	else
		if string.match(version, LUNE_STRING_MATCH) then
			return "Lune"
		elseif string.match(version, ZUNE_STRING_MATCH) then
			return "Zune"
		end
	end

	-- TODO: at some point in the future - figure out how to detect Lute!

	return "Unknown"
end

--[=[
	@within StdPolyfills.runtime

	Will error out if the current Runtime is not supported by Discord-Luau
]=]
function runtime.assertRuntime(): RuntimeValue
	local currentRuntime = runtime.getRuntime()

	if currentRuntime ~= "Lune" then
		error(`Discord-Luau currently doesn't support the '{currentRuntime}' runtime!`)
	end

	return currentRuntime
end

export type RuntimeValue = "Lune" | "Zune" | "Roblox" | "Luau" | "Lute" | "Unknown"
export type Runtime = typeof(runtime)

return runtime


--[=[
	@class StdPolyfills.serde
]=]

local runtime = require("./runtime")

local serde = {}

--[=[
	@within StdPolyfills.serde

	Encodes the given value using the given format.
]=]
function serde.encode(format: EncodeDecodeFormat, value: any, pretty: boolean?): string
	local target = runtime.assertRuntime()

	if target == "Lune" then
		return require("./lune/serde").encode(format, value, pretty)
	end

	error(`Out of reach`)
end

--[=[
	@within StdPolyfills.serde

	Decodes the given string using the given format into a lua value.
]=]
function serde.decode(format: EncodeDecodeFormat, encoded: buffer | string): any
	local target = runtime.assertRuntime()

	if target == "Lune" then
		return require("./lune/serde").decode(format, encoded)
	end

	error(`Out of reach`)
end

--[=[
	@within StdPolyfills.serde

	Decompresses the given string using the given format.
]=]
function serde.decompress(format: CompressDecompressFormat, s: buffer | string): string
	local target = runtime.assertRuntime()

	if target == "Lune" then
		return require("./lune/serde").decompress(format, s)
	end

	error(`Out of reach`)
end

--[=[
	@within StdPolyfills.serde

	Compresses the given string using the given format.
]=]
function serde.compress(format: CompressDecompressFormat, s: buffer | string, level: number?): string
	local target = runtime.assertRuntime()

	if target == "Lune" then
		return require("./lune/serde").compress(format, s, level)
	end

	error(`Out of reach`)
end

export type EncodeDecodeFormat = "json" | "yaml" | "toml"
export type CompressDecompressFormat = "brotli" | "gzip" | "lz4" | "zlib"

return serde


--[=[
	@class StdPolyfills.task
]=]

local runtime = require("./runtime")

local task = {}

--[=[
	@within StdPolyfills.task

	Stops a currently scheduled thread from resuming.
]=]
function task.cancel(thread: thread)
	local target = runtime.assertRuntime()

	if target == "Lune" then
		return require("./lune/task").cancel(thread)
	end

	error(`Out of reach`)
end

--[=[
	@within StdPolyfills.task

	Defers a thread or function to run at the end of the current task queue.
]=]
function task.defer<T...>(functionOrThread: thread | (T...) -> ...any, ...: T...): thread
	local target = runtime.assertRuntime()

	if target == "Lune" then
		return require("./lune/task").defer(functionOrThread, ...)
	end

	error(`Out of reach`)
end

--[=[
	@within StdPolyfills.task

	Delays a thread or function to run after duration seconds.
]=]
function task.delay<T...>(duration: number, functionOrThread: thread | (T...) -> ...any, ...: T...): thread
	local target = runtime.assertRuntime()

	if target == "Lune" then
		return require("./lune/task").delay(duration, functionOrThread, ...)
	end

	error(`Out of reach`)
end

--[=[
	@within StdPolyfills.task

	Instantly runs a thread or function.

	If the spawned task yields, the thread that spawned the task will resume, letting the spawned task run in the
		background.
]=]
function task.spawn<T...>(functionOrThread: thread | (T...) -> ...any, ...: T...): thread
	local target = runtime.assertRuntime()

	if target == "Lune" then
		return require("./lune/task").spawn(functionOrThread, ...)
	end

	error(`Out of reach`)
end

--[=[
	@within StdPolyfills.task

	Waits for at least the given amount of time.

	The minimum wait time possible when using task.wait is limited by the underlying OS sleep implementation. For most
		systems this means task.wait is accurate down to about 5 milliseconds or less.
]=]
function task.wait(duration: number?): number
	local target = runtime.assertRuntime()

	if target == "Lune" then
		return require("./lune/task").wait(duration)
	end

	error(`Out of reach`)
end

export type OS = "linux" | "macos" | "windows"

return task


--[=[
	@function Utilities.coroutineRun
	@within Utilities

	Creates a thread, resumes that thread - and handles errors if this resumed thread fails.

	Will return the thread spawned.

	@return thread
]=]
return function(func: () -> (), ...)
	local thread = coroutine.create(func)
	local status, result = coroutine.resume(thread, ...)

	if not status then
		error(result)
	end

	return thread
end


local template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"

--[=[
	@function Utilities.createGuild
	@within Utilities

	Generates a random Guid (v4) that we can use to apply a unique identifier to objects.

	@return string
]=]
return function()
	local guid = string.gsub(template, "[xy]", function(c)
		local value = (c == "x") and math.random(0, 15) or math.random(8, 11)

		return string.format("%x", value)
	end)

	return guid
end


--[=[
	@class Utilities

	Interface for the discord-luau Utilities package
]=]

return {
	validateKebabCase = require("@self/validateKebabCase"),
	createGuid = require("@self/createGuid"),
	coroutineRun = require("@self/coroutine/run"),
	tableReflect = require("@self/table/reflect"),
}


--[=[
	@function Utilities.tableReflect
	@within Utilities

	Will swap the K, V's, as well as keep the original K, Vs, for example:

	```luau
	local a = { a = 1, b = 2, c = 3 }

	tableReflect(a)

	--[[
	a = {
		-- default values
		a = 1,
		b = 2,
		c = 3,
		-- reflected values
		[1] = "a",
		[2] = "b",
		[3] = "c"
	}
	]]
	```

	@return thread
]=]
return function(source)
	local reversed = {}

	for i, v in source do
		reversed[v] = i
	end

	return setmetatable(source, {
		__index = reversed,
	})
end


--[=[
	@function Utilities.validateKebabCase
	@within Utilities

	Will validate a string to ensure that it's kebab-case, discord is sometimes very specific about the casing of
	text.

	Will return either true/false, will not error.

	@return thread
]=]
return function(source: string)
	if source == "" then
		return false
	end

	if string.sub(source, 1, 1) == "-" or string.sub(source, -1) == "-" then
		return false
	end

	local parts = string.split(source, "-")

	for _, part in ipairs(parts) do
		if part == "" then
			return false
		end

		if not string.match(part, "^[a-z0-9]+$") then
			return false
		end
	end

	return true
end


--[=[
	@class Voice
	@unreleased

	Interface for the discord-luau Voice package
]=]

return {}


local manager = require("@self/manager")
local shard = require("@self/shard")

export type Manager = manager.Manager

--[=[
	@class WebSocket

	Interface for the discord-luau WebSocket package
]=]

return {
	--[=[
		@prop manager WebSocket.Manager
		@within WebSocket
	]=]
	manager = manager,

	--[=[
		@prop shard WebSocket.Shard
		@within WebSocket
	]=]
	shard = shard,
}


import * as vscode from "vscode";
import * as utils from "./utils";
import path from "path";

export const isExternalFile = (path: string) => {
  return path.startsWith("http://") || path.startsWith("https://");
};

export const outputLocationForDefinition = (
  context: vscode.ExtensionContext,
  packageName: string,
) => {
  return vscode.Uri.joinPath(
    context.globalStorageUri,
    "definitions",
    `${packageName}.d.luau`,
  );
};

export const outputLocationForDocumentation = (
  context: vscode.ExtensionContext,
  documentationPath: string,
) => {
  return vscode.Uri.joinPath(
    context.globalStorageUri,
    "documentation",
    path.basename(documentationPath),
  );
};

export const anyFileIsMissing = async (uris: vscode.Uri[]) => {
  return (
    await Promise.all(uris.map(async (uri) => await utils.exists(uri)))
  ).includes(false);
};

/**
 * Check whether we should re-fetch definitions files from external sources.
 * We configure to re-fetch once a day.
 *
 * NOTE: This function has a side-effect, you should call once and re-use the result throughout your code.
 * Calling this function twice will return false the second time.
 */
export const shouldFetchDefinitions = (context: vscode.ExtensionContext) => {
  const lastUpdate = context.globalState.get<number>("last-api-update") ?? 0;
  const currentTimeMs = Date.now();

  if (currentTimeMs - lastUpdate > 1000 * 60 * 60 * 24) {
    context.globalState.update("last-api-update", currentTimeMs);
    return true;
  } else {
    return false;
  }
};

export interface DownloadFileDefinition {
  url: string;
  outputUri: vscode.Uri;
}

/**
 * Downloads a set of files from external sources to the provided locations.
 */
export const downloadExternalFiles = async (
  files: DownloadFileDefinition[],
) => {
  return vscode.window.withProgress(
    {
      location: vscode.ProgressLocation.Window,
      title: "Luau: Updating API Definitions",
      cancellable: false,
    },
    async () => {
      return Promise.all(
        files.map((info) =>
          fetch(info.url)
            .then((r) => {
              if (!r.ok) {
                return Promise.reject(
                  `fetching documentation error: ${r.status} ${r.statusText}`,
                );
              }

              return r.arrayBuffer();
            })
            .then((data) =>
              vscode.workspace.fs.writeFile(
                info.outputUri,
                new Uint8Array(data),
              ),
            ),
        ),
      ).catch((err) =>
        vscode.window.showErrorMessage(
          `Failed to retrieve API information: ${err}, cause: ${err.cause}`,
        ),
      );
    },
  );
};


import * as vscode from "vscode";
import * as os from "os";
import { fetch } from "undici";
import {
  CloseAction,
  CloseHandlerResult,
  ErrorAction,
  ErrorHandler,
  ErrorHandlerResult,
  Executable,
  LanguageClient,
  LanguageClientOptions,
  Message,
  ServerOptions,
  TransportKind,
} from "vscode-languageclient/node";

import {
  registerComputeBytecode,
  registerComputeCodeGen,
  registerComputeCompilerRemarks,
} from "./bytecode";

import { onTypeFormattingMiddleware } from "./onTypeFormattingMiddleware";

import { registerRequireGraph } from "./requireGraph";

import { registerViewInternalSource } from "./internalSource";

import * as roblox from "./roblox";
import * as utils from "./utils";
import {
  anyFileIsMissing,
  downloadExternalFiles,
  DownloadFileDefinition,
  isExternalFile,
  outputLocationForDefinition,
  outputLocationForDocumentation,
  shouldFetchDefinitions,
} from "./definitions";

export type PlatformContext = { client: LanguageClient | undefined };
export type AddArgCallback = (
  argument: string,
  mode?: "All" | "Prod" | "Debug",
) => void;

let client: LanguageClient | undefined = undefined;
let platformContext: PlatformContext = { client: undefined };
const clientDisposables: vscode.Disposable[] = [];

const CURRENT_FFLAGS =
  "https://clientsettingscdn.roblox.com/v1/settings/application?applicationName=PCStudioApp";
const FFLAG_KINDS = ["FFlag", "FInt", "DFFlag", "DFInt"];

type FFlags = Record<string, string>;
type FFlagsEndpoint = { applicationSettings: FFlags };

const getFFlags = async () => {
  return vscode.window.withProgress(
    {
      location: vscode.ProgressLocation.Window,
      title: "Luau: Fetching FFlags",
      cancellable: false,
    },
    () =>
      fetch(CURRENT_FFLAGS)
        .then((r) => r.json() as Promise<FFlagsEndpoint>)
        .then((r) => r.applicationSettings),
  );
};

const isAlphanumericUnderscore = (str: string) => {
  return /^[a-zA-Z0-9_]+$/.test(str);
};

const isCrashReportingEnabled = () => {
  const config = vscode.workspace.getConfiguration("luau-lsp.server");
  return config.get("crashReporting.enabled", false);
};

const DO_NOT_SHOW_CRASH_REPORTING_SUGGESTION_KEY =
  "doNotShowCrashReportingSuggestion";

const shouldShowEnableCrashReportsMessage = (
  context: vscode.ExtensionContext,
) => {
  return !context.globalState.get<boolean>(
    DO_NOT_SHOW_CRASH_REPORTING_SUGGESTION_KEY,
  );
};

class ClientErrorHandler implements ErrorHandler {
  private readonly restarts: number[];
  private recommendedCrashReporting: boolean = false;

  constructor(
    private context: vscode.ExtensionContext,
    private maxRestartCount: number,
  ) {
    this.restarts = [];
  }

  public error(
    _error: Error,
    _message: Message,
    count: number,
  ): ErrorHandlerResult {
    if (count && count <= 3) {
      return { action: ErrorAction.Continue };
    }
    return { action: ErrorAction.Shutdown };
  }

  public closed(): CloseHandlerResult {
    if (
      !this.recommendedCrashReporting &&
      !isCrashReportingEnabled() &&
      shouldShowEnableCrashReportsMessage(this.context)
    ) {
      this.recommendedCrashReporting = true;
      vscode.window
        .showInformationMessage(
          "The Luau Language server exited unexpected. Would you like to enable crash reporting?",
          "Enable",
          "Not now",
          "Do not show again",
        )
        .then((value) => {
          if (value === "Enable") {
            vscode.workspace
              .getConfiguration("luau-lsp.server")
              .update(
                "crashReporting.enabled",
                true,
                vscode.ConfigurationTarget.Global,
              );
          } else if (value === "Do not show again") {
            this.context.globalState.update(
              DO_NOT_SHOW_CRASH_REPORTING_SUGGESTION_KEY,
              true,
            );
          }
        });
    }

    this.restarts.push(Date.now());
    if (this.restarts.length <= this.maxRestartCount) {
      return { action: CloseAction.Restart };
    } else {
      const diff = this.restarts[this.restarts.length - 1] - this.restarts[0];
      if (diff <= 3 * 60 * 1000) {
        return {
          action: CloseAction.DoNotRestart,
          message: `The Luau Language server crashed ${this.maxRestartCount + 1} times in the last 3 minutes. The server will not be restarted. See the output for more information.`,
        };
      } else {
        this.restarts.shift();
        return { action: CloseAction.Restart };
      }
    }
  }
}

const handleExternalFiles = async (
  context: vscode.ExtensionContext,
  definitionFiles: {
    [packageName: string]: string;
  },
  documentationFiles: string[],
  builtinDefinitionFiles: {
    [packageName: string]: DownloadFileDefinition;
  },
  builtinDocumentationFiles: DownloadFileDefinition[],
) => {
  const finalDefinitionFiles = new Map<string, string>();

  // Determine a list of external files to fetch
  const externalFiles: DownloadFileDefinition[] = [];
  for (let [packageName, definitionPath] of Object.entries(definitionFiles)) {
    if (isExternalFile(definitionPath)) {
      const outputLocation = outputLocationForDefinition(context, packageName);
      externalFiles.push({
        url: definitionPath,
        outputUri: outputLocation,
      });
      finalDefinitionFiles.set(packageName, outputLocation.fsPath);
    } else {
      finalDefinitionFiles.set(packageName, definitionPath);
    }
  }
  documentationFiles = documentationFiles.map((documentationPath) => {
    if (isExternalFile(documentationPath)) {
      const outputLocation = outputLocationForDocumentation(
        context,
        documentationPath,
      );
      externalFiles.push({
        url: documentationPath,
        outputUri: outputLocation,
      });
      return outputLocation.fsPath;
    } else {
      return documentationPath;
    }
  });

  for (const [packageName, downloadDefinition] of Object.entries(
    builtinDefinitionFiles,
  )) {
    if (!finalDefinitionFiles.has(packageName)) {
      externalFiles.push(downloadDefinition);
      finalDefinitionFiles.set(
        packageName,
        downloadDefinition.outputUri.fsPath,
      );
    }
  }

  if (builtinDocumentationFiles) {
    externalFiles.push(...builtinDocumentationFiles);
    documentationFiles = documentationFiles.concat(
      builtinDocumentationFiles.map((info) => info.outputUri.fsPath),
    );
  }

  if (externalFiles) {
    const mustUpdate = await anyFileIsMissing(
      externalFiles.map((info) => info.outputUri),
    );
    if (mustUpdate || shouldFetchDefinitions(context)) {
      try {
        await downloadExternalFiles(externalFiles);
      } catch (err) {
        vscode.window.showWarningMessage(
          "Failed to donwload API information: " + err,
        );
      }
    }
  }

  return {
    definitionFiles: finalDefinitionFiles,
    documentationFiles,
    externalFiles,
  };
};

const startLanguageServer = async (context: vscode.ExtensionContext) => {
  for (const disposable of clientDisposables) {
    disposable.dispose();
  }
  clientDisposables.splice(0, clientDisposables.length); // empty the list
  if (client) {
    await client.stop();
  }

  console.log("Starting Luau Language Server");

  const args = ["lsp"];
  const debugArgs = ["lsp"];

  const addArg = (argument: string, mode: "All" | "Prod" | "Debug" = "All") => {
    if (mode === "All" || mode === "Prod") {
      args.push(argument);
    }
    if (mode === "All" || mode === "Debug") {
      debugArgs.push(argument);
    }
  };

  const {
    definitions: builtinDefinitionFiles,
    documentation: builtinDocumentationFiles,
  } = await roblox.preLanguageServerStart(context);

  const typesConfig = vscode.workspace.getConfiguration("luau-lsp.types");

  // Load extra type definitions
  // TODO: deprecate and remove support of array-based definitionFiles configuration
  let definitionFilesConfig =
    typesConfig.get<{ [packageName: string]: string } | string[]>(
      "definitionFiles",
    ) ?? {};

  if (Array.isArray(definitionFilesConfig)) {
    definitionFilesConfig = Object.fromEntries(
      definitionFilesConfig.map((path, index) => ["roblox" + index, path]),
    );
  }

  const documentationFilesConfig =
    typesConfig.get<string[]>("documentationFiles") ?? [];

  const result = await handleExternalFiles(
    context,
    definitionFilesConfig,
    documentationFilesConfig,
    builtinDefinitionFiles ?? {},
    builtinDocumentationFiles ?? [],
  );

  for (let [packageName, definitionPath] of result.definitionFiles) {
    definitionPath = utils.resolvePath(definitionPath);
    let uri;
    if (vscode.workspace.workspaceFolders) {
      uri = utils.resolveUri(
        vscode.workspace.workspaceFolders[0].uri,
        definitionPath,
      );
    } else {
      uri = vscode.Uri.file(definitionPath);
    }
    if (await utils.exists(uri)) {
      addArg(`--definitions:${packageName}=${uri.fsPath}`);
    } else {
      vscode.window.showWarningMessage(
        `Definitions file '${packageName}' at ${definitionPath} does not exist, types will not be provided from this file`,
      );
    }
  }

  // Load extra documentation files
  for (let documentationPath of result.documentationFiles) {
    documentationPath = utils.resolvePath(documentationPath);
    let uri;
    if (vscode.workspace.workspaceFolders) {
      uri = utils.resolveUri(
        vscode.workspace.workspaceFolders[0].uri,
        documentationPath,
      );
    } else {
      uri = vscode.Uri.file(documentationPath);
    }
    if (await utils.exists(uri)) {
      addArg(`--docs=${uri.fsPath}`);
    } else {
      vscode.window.showWarningMessage(
        `Documentations file at ${documentationPath} does not exist`,
      );
    }
  }

  // Handle FFlags
  const fflags: FFlags = {};
  const fflagsConfig = vscode.workspace.getConfiguration("luau-lsp.fflags");

  if (!fflagsConfig.get<boolean>("enableByDefault")) {
    addArg("--no-flags-enabled");
  }

  // Sync FFlags with upstream
  if (fflagsConfig.get<boolean>("sync")) {
    try {
      const currentFlags = await getFFlags();
      if (currentFlags) {
        for (const [name, value] of Object.entries(currentFlags)) {
          for (const kind of FFLAG_KINDS) {
            if (name.startsWith(`${kind}Luau`)) {
              // Remove the "FFlag" part from the name
              fflags[name.substring(kind.length)] = value;
            }
          }
        }
      }
    } catch (err) {
      vscode.window.showWarningMessage(
        "Failed to fetch current Luau FFlags: " + err,
      );
    }
  }

  // Enable new solver
  if (fflagsConfig.get<boolean>("enableNewSolver")) {
    fflags["LuauSolverV2"] = "true";
  }

  // Handle overrides
  const overridenFFlags = fflagsConfig.get<FFlags>("override");
  if (overridenFFlags) {
    for (let [name, value] of Object.entries(overridenFFlags)) {
      if (!isAlphanumericUnderscore(name)) {
        vscode.window.showWarningMessage(
          `Invalid FFlag name: '${name}'. It can only contain alphanumeric characters`,
        );
      }

      name = name.trim();
      value = value.trim();

      // Strip kind prefix if it was included
      for (const kind of FFLAG_KINDS) {
        if (name.startsWith(`${kind}`)) {
          name = name.substring(kind.length);
        }
      }

      // Validate that the name and value is valid
      if (name.length > 0 && value.length > 0) {
        fflags[name] = value;
      }
    }
  }

  const serverConfiguration =
    vscode.workspace.getConfiguration("luau-lsp.server");

  const serverBinConfig = serverConfiguration.get("path", "").trim();
  const serverBinUri =
    vscode.workspace.workspaceFolders &&
    vscode.workspace.workspaceFolders.length > 0
      ? utils.resolveUri(
          vscode.workspace.workspaceFolders[0].uri,
          serverBinConfig,
        )
      : vscode.Uri.file(serverBinConfig);
  let serverBinPath;

  if (serverBinConfig !== "" && (await utils.exists(serverBinUri))) {
    serverBinPath = serverBinUri.fsPath;
  } else {
    if (serverBinConfig !== "") {
      vscode.window.showWarningMessage(
        `Server binary at path \`${serverBinUri.fsPath}\` does not exist, falling back to bundled binary`,
      );
    }
    serverBinPath = vscode.Uri.joinPath(
      context.extensionUri,
      "bin",
      os.platform() === "win32" ? "server.exe" : "server",
    ).fsPath;
  }

  const transport =
    serverConfiguration.get<"stdio" | "pipe">(
      "communicationChannel",
      "stdio",
    ) === "pipe"
      ? TransportKind.pipe
      : TransportKind.stdio;

  const delayStartup = serverConfiguration.get<boolean>("delayStartup", false);
  if (delayStartup) {
    addArg("--delay-startup");
  }

  if (isCrashReportingEnabled()) {
    addArg("--enable-crash-reporting");
    addArg(
      `--crash-report-directory=${vscode.Uri.joinPath(context.globalStorageUri, "sentry").fsPath}`,
    );
  }

  // Handle base luaurc
  const baseLuaurcConfig = serverConfiguration.get<string>("baseLuaurc");
  if (baseLuaurcConfig) {
    const baseLuaurcPath = utils.resolvePath(baseLuaurcConfig);
    let uri;
    if (vscode.workspace.workspaceFolders) {
      uri = utils.resolveUri(
        vscode.workspace.workspaceFolders[0].uri,
        baseLuaurcPath,
      );
    } else {
      uri = vscode.Uri.file(baseLuaurcPath);
    }
    if (await utils.exists(uri)) {
      addArg(`--base-luaurc=${uri.fsPath}`);
    } else {
      vscode.window
        .showWarningMessage(
          `Base .luaurc file at ${baseLuaurcPath} does not exist`,
          "Configure Settings",
        )
        .then((action) => {
          if (action === "Configure Settings") {
            vscode.commands.executeCommand(
              "workbench.action.openSettings",
              "luau-lsp.server.baseLuaurc",
            );
          }
        });
    }
  }

  const run: Executable = {
    command: serverBinPath,
    args,
    transport,
  };

  // If debugging, run the locally build extension, with local type definitions file
  const debug: Executable = {
    command: process.env["LUAU_LSP_SERVER_PATH"]
      ? vscode.Uri.file(process.env["LUAU_LSP_SERVER_PATH"]).fsPath
      : serverBinPath,
    args: debugArgs,
    transport,
  };

  const serverOptions: ServerOptions = { run, debug };

  const clientOptions: LanguageClientOptions = {
    documentSelector: [
      { language: "lua", scheme: "file" },
      { language: "luau", scheme: "file" },
      { language: "lua", scheme: "untitled" },
      { language: "luau", scheme: "untitled" },
    ],
    diagnosticPullOptions: {
      onChange: vscode.workspace
        .getConfiguration("luau-lsp.diagnostics")
        .get("pullOnChange", true),
      onSave: vscode.workspace
        .getConfiguration("luau-lsp.diagnostics")
        .get("pullOnSave", true),
    },
    initializationOptions: {
      fflags,
    },
    markdown: {
      supportHtml: true,
    },
    errorHandler: new ClientErrorHandler(context, 4),
    middleware: {
      provideOnTypeFormattingEdits: onTypeFormattingMiddleware,
    },
  };

  client = new LanguageClient(
    "luau",
    "Luau Language Server",
    serverOptions,
    clientOptions,
  );

  platformContext.client = client;

  // Register commands
  client.onNotification("$/command", (params) => {
    vscode.commands.executeCommand(params.command, params.data);
  });

  clientDisposables.push(
    vscode.commands.registerCommand(
      "luau-lsp.rename",
      async (
        uriString: string,
        position: { line: number; character: number },
      ) => {
        const uri = vscode.Uri.parse(uriString);
        const pos = new vscode.Position(position.line, position.character);
        const editor = vscode.window.activeTextEditor;
        if (editor && editor.document.uri.toString() === uri.toString()) {
          editor.selection = new vscode.Selection(pos, pos);
          await vscode.commands.executeCommand("editor.action.rename");
        }
      },
    ),
  );

  clientDisposables.push(...registerComputeBytecode(context, client));
  clientDisposables.push(...registerComputeCompilerRemarks(context, client));
  clientDisposables.push(...registerComputeCodeGen(context, client));
  clientDisposables.push(...registerRequireGraph(context, client));
  clientDisposables.push(...registerViewInternalSource(context, client));
  clientDisposables.push(
    vscode.commands.registerCommand("luau-lsp.openWalkthrough", () => {
      return vscode.commands.executeCommand(
        "workbench.action.openWalkthrough",
        "JohnnyMorganz.luau-lsp#getting-started",
        false,
      );
    }),
  );
  clientDisposables.push(
    vscode.commands.registerCommand("luau-lsp.updateApi", async () => {
      await downloadExternalFiles(result.externalFiles);
      vscode.window
        .showInformationMessage(
          "API Types have been updated, reload server to take effect.",
          "Reload Language Server",
        )
        .then((command) => {
          if (command === "Reload Language Server") {
            vscode.commands.executeCommand("luau-lsp.reloadServer");
          }
        });
    }),
  );

  console.log("LSP Setup");
  await client.start();
};

export async function activate(context: vscode.ExtensionContext) {
  console.log("Luau LSP activated");

  await roblox.onActivate(platformContext, context);

  context.subscriptions.push(
    vscode.commands.registerCommand("luau-lsp.reloadServer", async () => {
      vscode.window.showInformationMessage("Reloading Language Server");
      await startLanguageServer(context);
    }),
    vscode.commands.registerCommand("luau-lsp.flushTimeTrace", async () => {
      if (client) {
        client.sendNotification("$/flushTimeTrace");
      }
    }),
  );

  context.subscriptions.push(
    vscode.workspace.onDidChangeConfiguration((e) => {
      if (e.affectsConfiguration("luau-lsp.server")) {
        vscode.window
          .showInformationMessage(
            "Luau LSP server configuration has changed, reload server for this to take effect.",
            "Reload Language Server",
          )
          .then((command) => {
            if (command === "Reload Language Server") {
              vscode.commands.executeCommand("luau-lsp.reloadServer");
            }
          });
      } else if (
        e.affectsConfiguration("luau-lsp.fflags") ||
        e.affectsConfiguration("luau-lsp.completion.enableFragmentAutocomplete")
      ) {
        vscode.window
          .showInformationMessage(
            "Luau FFlags have been changed, reload server for this to take effect.",
            "Reload Language Server",
          )
          .then((command) => {
            if (command === "Reload Language Server") {
              vscode.commands.executeCommand("luau-lsp.reloadServer");
            }
          });
      } else if (
        e.affectsConfiguration("luau-lsp.types") ||
        e.affectsConfiguration("luau-lsp.platform.type")
      ) {
        vscode.window
          .showInformationMessage(
            "Luau type definitions have been changed, reload server for this to take effect.",
            "Reload Language Server",
          )
          .then((command) => {
            if (command === "Reload Language Server") {
              vscode.commands.executeCommand("luau-lsp.reloadServer");
            }
          });
      }
    }),
  );

  await startLanguageServer(context);

  await roblox.postLanguageServerStart(platformContext, context);
}

export async function deactivate() {
  return Promise.allSettled([
    ...roblox.onDeactivate(),
    client?.stop(),
    clientDisposables.map((disposable) => disposable.dispose()),
  ]);
}


import * as vscode from "vscode";
import { ProvideOnTypeFormattingEditsSignature } from "vscode-languageclient";

export function isQuoteConversionEdit(edits: vscode.TextEdit[]): boolean {
  return edits.some((edit) => edit.newText === "`");
}

export function shouldHandleQuoteConversion(
  edits: vscode.TextEdit[] | null | undefined,
  ch: string,
): boolean {
  if (!edits || edits.length === 0) {
    return false;
  }
  if (ch !== "{") {
    return false;
  }
  return isQuoteConversionEdit(edits);
}

export async function onTypeFormattingMiddleware(
  document: vscode.TextDocument,
  position: vscode.Position,
  ch: string,
  options: vscode.FormattingOptions,
  token: vscode.CancellationToken,
  next: ProvideOnTypeFormattingEditsSignature,
): Promise<vscode.TextEdit[] | null | undefined> {
  const edits = await next(document, position, ch, options, token);

  if (!edits || !shouldHandleQuoteConversion(edits, ch)) {
    return edits;
  }

  const editor = vscode.window.activeTextEditor;
  if (!editor || editor.document !== document) {
    // Not the active document: don't bother with cursor shenanigans
    return edits;
  }

  // Apply text edits and restore cursor position
  const cursorPos = editor.selection.active;

  const workspaceEdit = new vscode.WorkspaceEdit();
  for (const edit of edits) {
    workspaceEdit.replace(document.uri, edit.range, edit.newText);
  }
  await vscode.workspace.applyEdit(workspaceEdit);

  const newSelection = new vscode.Selection(cursorPos, cursorPos);
  editor.selection = newSelection;

  // Return undefined to prevent double application
  return undefined;
}


/* --------------------------------------------------------------------------------------------
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License. See License.txt in the project root for license information.
 * ------------------------------------------------------------------------------------------ */

import * as vscode from "vscode";
import * as path from "path";

export let doc: vscode.TextDocument;
export let editor: vscode.TextEditor;
export let documentEol: string;
export let platformEol: string;

/**
 * Activates the vscode.lsp-sample extension
 */
export async function activate(docUri: vscode.Uri) {
  const ext = vscode.extensions.getExtension("johnnymorganz.luau-lsp")!;
  await ext.activate();
  try {
    doc = await vscode.workspace.openTextDocument(docUri);
    editor = await vscode.window.showTextDocument(doc);
    await sleep(2000); // Wait for server activation
  } catch (e) {
    console.error(e);
  }
}

async function sleep(ms: number) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export const getDocPath = (p: string) => {
  return path.resolve(__dirname, "../../testFixture", p);
};
export const getDocUri = (p: string) => {
  return vscode.Uri.file(getDocPath(p));
};

export async function setTestContent(content: string): Promise<boolean> {
  const all = new vscode.Range(
    doc.positionAt(0),
    doc.positionAt(doc.getText().length),
  );
  return editor.edit((eb) => eb.replace(all, content));
}


import * as vscode from "vscode";
import * as path from "path";
import * as os from "os";

export const basenameUri = (uri: vscode.Uri): string => {
  return path.basename(uri.fsPath);
};

export const exists = (uri: vscode.Uri): Thenable<boolean> => {
  return vscode.workspace.fs.stat(uri).then(
    () => true,
    () => false,
  );
};

export const resolveUri = (uri: vscode.Uri, ...paths: string[]): vscode.Uri => {
  return vscode.Uri.file(path.resolve(uri.fsPath, ...paths));
};

/// Expand ~ at the start of the path
export const resolvePath = (mainPath: string): string => {
  if (mainPath.startsWith("~/") || mainPath.startsWith("~\\")) {
    return path.join(os.homedir(), mainPath.slice(2));
  }
  return mainPath;
};


--!strict
--- Script to publish the Luau Language Server companion plugin to Roblox

local AssetService = game:GetService("AssetService")

local ASSET_ID = 10913122509
local CREATOR_ID = 68136726

print("Executing AssetService:CreateAssetVersionAsync")

local result, number =
	AssetService:CreateAssetVersionAsync(game.ReplicatedStorage.Plugin, Enum.AssetType.Plugin, ASSET_ID, {
		CreatorId = CREATOR_ID,
	})

print(result, number)


--!strict

--- Asset IDs for images used in the plugin's toolbar and UI.
--- These are Roblox asset IDs pointing to uploaded images.

local Assets = {}

--- Default icon
Assets.Icon = "rbxassetid://85321628471210"

--- Icon with a green square indicating connection
Assets.ConnectedIcon = "rbxassetid://126078034992458"

--- Icon for the settings button in the toolbar.
Assets.SettingsIcon = "rbxassetid://111753866426868"

return Assets


--!strict

--- Tracks changes to Roblox Instances and notifies listeners with filtering based on include paths.
--- Monitors name and parent property changes, as well as new descendants.
--- Encodes Roblox Instances into a serializable format for the language server.

local Log = require(script.Parent.Utils.Log)
local Signal = require(script.Parent.Utils.Signal)
local types = require(script.Parent.types)

local InstanceTracker = {}
InstanceTracker.__index = InstanceTracker

--- Internal data structure for an InstanceTracker instance.
type InstanceTrackerData = {
	--- Map of tracked instances to their associated connections.
	_connections: { [Instance]: { RBXScriptConnection } },
	--- Function that returns the list of instances to include.
	_getIncludeList: () -> { Instance },
	--- Signal fired when any tracked instance changes.
	onChanged: Signal.Signal<Instance>,
}

--- Tracks changes to Roblox Instances and notifies listeners.
export type InstanceTracker = typeof(setmetatable({} :: InstanceTrackerData, InstanceTracker))

--- Creates a new InstanceTracker instance.
--- @param getIncludeList -- Function that returns the list of instances to include.
--- @return InstanceTracker -- A new InstanceTracker ready to watch instances.
function InstanceTracker.new(getIncludeList: () -> { Instance }): InstanceTracker
	local self = {
		_connections = {},
		_getIncludeList = getIncludeList,
		onChanged = Signal.new() :: Signal.Signal<Instance>,
	}
	return setmetatable(self, InstanceTracker)
end

--- Starts tracking changes to a specific Instance.
--- Monitors the Name and Parent properties for changes.
--- @param self -- The InstanceTracker instance.
--- @param instance -- The Instance to track.
function InstanceTracker.track(self: InstanceTracker, instance: Instance): ()
	local nameConnection = instance:GetPropertyChangedSignal("Name"):Connect(function()
		Log.debug(`Instance {instance:GetFullName()} name changed`)
		self.onChanged:fire(instance)
	end)
	local parentConnection = instance:GetPropertyChangedSignal("Parent"):Connect(function()
		Log.debug(`Instance {instance:GetFullName()} parent changed`)
		self.onChanged:fire(instance)

		if instance.Parent == nil or not self:shouldInclude(instance) then
			-- Instance is no longer tracked, stop tracking it
			self:untrack(instance)
		end
	end)
	self._connections[instance] = { nameConnection, parentConnection }
end

--- Stops tracking changes to a specific Instance and its children.
--- Disconnects all associated event connections.
--- @param self -- The InstanceTracker instance.
--- @param instance -- The Instance to stop tracking.
function InstanceTracker.untrack(self: InstanceTracker, instance: Instance): ()
	Log.debug(`Un-tracking changes for {instance:GetFullName()}`)

	local connections = self._connections[instance]
	if not connections then
		-- We are already not tracking this instance
		return
	end

	for _, child in instance:GetChildren() do
		self:untrack(child)
	end

	for _, connection in connections do
		connection:Disconnect()
	end
	self._connections[instance] = nil
end

--- Starts tracking all instances in the provided include list.
--- Also watches for new descendants being added to the DataModel.
--- @param self -- The InstanceTracker instance.
function InstanceTracker.watchAll(self: InstanceTracker): ()
	Log.debug("Watching changes")

	-- Track initial instances
	for _, instance in self._getIncludeList() do
		self:track(instance)

		for _, descendant in instance:GetDescendants() do
			self:track(descendant)
		end
	end

	-- Track new instances
	self._connections[game] = {
		game.DescendantAdded:Connect(function(instance)
			if self:shouldInclude(instance) then
				self:track(instance)
				self.onChanged:fire(instance)
			end
		end),
	}
end

--- Stops tracking all instances and clears all connections.
--- Call this when disconnecting from the language server.
--- @param self -- The InstanceTracker instance.
function InstanceTracker.unwatchAll(self: InstanceTracker): ()
	-- Collect instances first to avoid modifying table while iterating
	local instances = {}
	for instance in self._connections do
		table.insert(instances, instance)
	end
	for _, instance in instances do
		self:untrack(instance)
	end
	self._connections = {}
end

--- Determines whether an Instance should be included in the encoded tree.
--- An instance is included if it is within, contains, or is one of the
--- provided include paths.
--- @param instance -- The Instance to check.
--- @param includeList? -- The list of instances to include.
--- @return boolean -- True if the instance should be included.
function InstanceTracker.shouldInclude(self: InstanceTracker, instance: Instance, includeList: { Instance }?): boolean
	-- Ignore instances that are not part of any tree
	if instance.Parent == nil then
		return false
	end

	-- Check if this instance is within, contains, or is an included tree
	for _, includedInstance in (includeList or self._getIncludeList()) :: { Instance } do
		if
			instance == includedInstance
			or instance:IsDescendantOf(includedInstance)
			or instance:IsAncestorOf(includedInstance)
		then
			return true
		end
	end

	-- Ignore all instances that are not explicitly included
	return false
end

--- Recursively encodes an Instance and its children into a serializable table.
--- Only includes children that pass the `shouldInclude` filter.
--- @param self -- The InstanceTracker instance.
--- @param instance -- The Instance to encode.
--- @param includeList -- The list of instances to include.
--- @param instanceFilePaths -- A mapping of instances to their Luau file paths.
--- @return types.EncodedInstance -- The encoded representation.
function InstanceTracker.encode(
	self: InstanceTracker,
	instance: Instance,
	includeList: { Instance },
	instanceFilePaths: { [Instance]: { string } }
): types.EncodedInstance
	Log.debug(`Encoding {instance:GetFullName()}`)

	local encoded: types.EncodedInstance = {
		Name = instance.Name,
		ClassName = instance.ClassName,
		FilePaths = instanceFilePaths[instance] or {},
		Children = {},
	}

	for _, child in instance:GetChildren() do
		if not self:shouldInclude(child, includeList) then
			continue
		end

		table.insert(encoded.Children, self:encode(child, includeList, instanceFilePaths))
	end

	return encoded
end

--- Utility function to encode the entire DataModel into a serializable table.
--- @param self -- The InstanceTracker instance.
--- @param instanceFilePaths -- A mapping of instances to their Luau file paths.
--- @return types.EncodedInstance -- The encoded representation of the DataModel.
function InstanceTracker.encodeAll(
	self: InstanceTracker,
	instanceFilePaths: { [Instance]: { string } }
): types.EncodedInstance
	return self:encode(game, self._getIncludeList(), instanceFilePaths)
end

--- Cleans up the InstanceTracker.
--- Stops tracking all instances and destroys the signal.
--- Call this when the plugin is unloading.
--- @param self -- The InstanceTracker instance.
function InstanceTracker.unload(self: InstanceTracker): ()
	self:unwatchAll()
	self.onChanged:destroy()
end

return InstanceTracker


--- Configuration for the Luau Language Server
--- https://devforum.roblox.com/t/luau-language-server-for-external-editors/2185389
--- Make sure that `luau-lsp.plugin.enabled` is set to `true` in VSCode for the plugin to connect

return {
	--- The host to connect to the language server.
	host = "http://localhost",

	--- The port to connect to the language server.
	--- In VSCode, this is configured using `luau-lsp.plugin.port`
	--- Note, this port is *different* to your Rojo port
	port = 3667,

	--- Whether the plugin should automatically connect to the language server when Studio starts up
	startAutomatically = false,

	--- A list of Instances to track for changes.
	--- When any descendants of these instances change, an update event is sent to the language server
	include = {
		game:GetService("Workspace"),
		game:GetService("Players"),
		game:GetService("Lighting"),
		game:GetService("ReplicatedFirst"),
		game:GetService("ReplicatedStorage"),
		game:GetService("ServerScriptService"),
		game:GetService("ServerStorage"),
		game:GetService("StarterGui"),
		game:GetService("StarterPack"),
		game:GetService("StarterPlayer"),
		game:GetService("SoundService"),
		game:GetService("Chat"),
		game:GetService("LocalizationService"),
		game:GetService("TestService"),
	},

	--- The log level to use for the language server.
	--- Valid values are "DEBUG", "INFO", "WARN", "ERROR"
	logLevel = "INFO",
}


--!strict

--- Debounce utility for rate-limiting function calls.
--- Delays callback execution until a period of inactivity.
---
--- Usage:
--- ```lua
--- local debounce = Debounce.new(0.5, function()
---     print("Called after 0.5s of inactivity")
--- end)
--- debounce:call() -- Starts/resets the timer
--- debounce:cancel() -- Cancels pending execution
--- ```

local Debounce = {}
Debounce.__index = Debounce

--- Internal data structure for a Debounce instance.
type DebounceData = {
	--- The delay in seconds before the callback is invoked.
	delaySeconds: number,
	--- The function to call after the delay.
	callback: () -> (),
	--- The currently pending task, if any.
	pendingTask: thread?,
}

--- A debounce controller that delays function execution.
export type Debounce = typeof(setmetatable({} :: DebounceData, Debounce))

--- Creates a new Debounce instance.
--- @param delaySeconds -- The delay in seconds before invoking the callback.
--- @param callback -- The function to call after the delay period.
--- @return Debounce -- A new Debounce instance.
function Debounce.new(delaySeconds: number, callback: () -> ()): Debounce
	local self = {
		delaySeconds = delaySeconds,
		callback = callback,
		pendingTask = nil,
	}
	return setmetatable(self, Debounce)
end

--- Schedules the callback to run after the delay.
--- If called again before the delay expires, the timer is reset.
--- @param self -- The Debounce instance.
--- @return thread -- The scheduled task thread.
function Debounce.call(self: Debounce): thread
	self:cancel()

	local pendingTask = task.delay(self.delaySeconds, function()
		self.pendingTask = nil
		self.callback()
	end)
	self.pendingTask = pendingTask
	return pendingTask
end

--- Cancels any pending callback execution.
--- @param self -- The Debounce instance.
function Debounce.cancel(self: Debounce): ()
	if self.pendingTask then
		task.cancel(self.pendingTask)
		self.pendingTask = nil
	end
end

return Debounce


--!strict

--- Logging utility for the Luau Language Server plugin.
--- Provides leveled logging with configurable output filtering.
---
--- Usage:
--- ```lua
--- Log.setLevel(Log.Level.DEBUG)
--- Log.info("Connected to server")
--- Log.debug("Processing", itemCount, "items")
--- ```

local Log = {}

--- Numeric log level constants for filtering messages.
--- Lower values are more verbose.
Log.Level = {
	DEBUG = 1,
	INFO = 2,
	WARN = 3,
	ERROR = 4,
}

--- Maps log levels to their output functions.
Log._levelMethod = {
	[Log.Level.DEBUG] = function(...: any)
		print(...)
	end,
	[Log.Level.INFO] = function(...: any)
		print(...)
	end,
	[Log.Level.WARN] = function(...: any)
		warn(...)
	end,
	[Log.Level.ERROR] = function(...: any)
		error(...)
	end,
}

--- Prefix prepended to all log messages for identification.
Log._levelPrefix = {
	[Log.Level.DEBUG] = "[Luau LSP] DEBUG:",
	[Log.Level.INFO] = "[Luau LSP] INFO: ",
	[Log.Level.WARN] = "[Luau LSP] WARN: ",
	[Log.Level.ERROR] = "[Luau LSP] ERROR:",
}

--- The current minimum log level. Messages below this level are suppressed.
Log._currentLevel = Log.Level.INFO

--- Sets the minimum log level for message output.
--- Messages with a level below this threshold will be suppressed.
--- @param level -- The numeric log level (use Log.Level constants).
function Log.setLevel(level: number): ()
	Log._currentLevel = math.clamp(level, Log.Level.DEBUG, Log.Level.ERROR)
end

--- Internal logging function that checks the level and outputs the message.
--- @param level -- The numeric log level of this message.
--- @param ... -- The values to log.
function Log._log(level: number, ...: any): ()
	if level < Log._currentLevel then
		return
	end

	local method = Log._levelMethod[level] or Log._levelMethod[Log.Level.INFO]
	local levelPrefix = Log._levelPrefix[level] or Log._levelPrefix[Log.Level.INFO]
	method(levelPrefix, ...)
end

--- Logs a debug message. Only shown when log level is DEBUG.
--- @param ... -- The values to log.
function Log.debug(...): ()
	Log._log(Log.Level.DEBUG, ...)
end

--- Logs an informational message. Shown when log level is INFO or lower.
--- @param ... -- The values to log.
function Log.info(...): ()
	Log._log(Log.Level.INFO, ...)
end

--- Logs a warning message. Shown when log level is WARN or lower.
--- @param ... -- The values to log.
function Log.warn(...): ()
	Log._log(Log.Level.WARN, ...)
end

--- Logs an error message and halts execution.
--- Always shown regardless of log level.
--- @param ... -- The values to include in the error message.
function Log.error(...): never
	-- In order to type this as never, we can't use _log here
	error(`{Log._levelPrefix[Log.Level.ERROR]} {table.concat({ ... }, " ")}`, 2)
end

return Log


--!strict

--- A dead simple strictly typed signal/event utility for managing listeners.
--- Provides a reusable pattern for registering callbacks and firing events.
---
--- Usage:
--- ```lua
--- local signal = Signal.new()
--- local disconnect = signal:connect(function(value)
---     print("Received:", value)
--- end)
--- signal:fire("Hello") -- Prints: Received: Hello
--- disconnect() -- Unregisters the listener
--- ```

local Log = require(script.Parent.Log)

local Signal = {}
Signal.__index = Signal

--- Internal data structure for a Signal instance.
type SignalData<T...> = {
	--- List of registered listener callbacks.
	_listeners: { (T...) -> () },
}

--- A signal that can have listeners connected and fire events.
export type Signal<T...> = typeof(setmetatable({} :: SignalData<T...>, Signal))

--- Creates a new Signal instance.
--- @return Signal -- A new Signal ready for listeners.
function Signal.new<T...>(): Signal<T...>
	local self = {
		_listeners = {},
	}
	return setmetatable(self, Signal)
end

--- Registers a callback to be invoked when the signal fires.
--- @param self -- The Signal instance.
--- @param listener -- The callback function to register.
--- @return () -> () -- A function to unregister the listener.
function Signal.connect<T...>(self: Signal<T...>, listener: (T...) -> ()): () -> ()
	table.insert(self._listeners, listener)
	return function()
		local index = table.find(self._listeners, listener)
		if index then
			table.remove(self._listeners, index)
		end
	end
end

--- Invokes all registered listeners with the provided arguments.
--- @param self -- The Signal instance.
--- @param ... -- The arguments to pass to each listener.
function Signal.fire<T...>(self: Signal<T...>, ...: T...): ()
	for _, listener in self._listeners do
		xpcall(listener, function(err)
			Log.warn(`Error calling listener: {err}`)
		end, ...)
	end
end

--- Removes all listeners from the signal.
--- Call this when the signal is no longer needed to prevent memory leaks.
--- @param self -- The Signal instance.
function Signal.destroy<T...>(self: Signal<T...>): ()
	table.clear(self._listeners)
end

return Signal


declare class RemodelInstance
	Name: string
	ClassName: string
	Parent: RemodelInstance
	function Destroy(self): ()
	function Clone(self): RemodelInstance
	function GetChildren(self): { RemodelInstance }
	function GetDescendants(self): { RemodelInstance }
	function FindFirstChild(self, name: string): RemodelInstance?
end

declare class RemodelDataModel extends RemodelInstance
	function GetService(self, name: string): RemodelInstance
end

type SupportedType = "String" | "Content" | "Bool" | "Float64" | "Float32" | "Int64" | "Int32"

export type Remodel = {
	readPlaceFile: (path: string) -> RemodelDataModel,
	readModelFile: (path: string) -> { RemodelInstance },
	readPlaceAsset: (assetId: string) -> RemodelDataModel,
	readModelAsset: (assetId: string) -> { RemodelInstance },
	writePlaceFile: (path: string, instance: RemodelDataModel)	-> (),
	writeModelFile: (path: string, instance: RemodelInstance) -> (),
	writeExistingPlaceAsset: (instance: RemodelDataModel, assetId: string) -> (),
	writeExistingModelAsset: (instance: RemodelInstance, assetId: string) -> (),
	getRawProperty: (instance: RemodelInstance, name: string) -> any?,
	setRawProperty: (instance: RemodelInstance, name: string, type: SupportedType, value: any) -> (),
	readFile: (path: string) -> string,
	readDir: (path: string) -> { string },
	writeFile: (path: string, contents: string) -> (),
	removeFile: (path: string) -> (),
	createDirAll: (path: string) -> (),
	removeDir: (path: string) -> (),
	isFile: (path: string) -> boolean,
	isDir: (path: string) -> boolean,
}

export type JSON = {
	fromString: (source: string) -> any,
	toString: (value: any) -> string,
	toStringPretty: (value: any, indent: string?) -> string,
}

declare Instance: {
	new: (className: string) -> RemodelInstance,
}

declare remodel: Remodel
declare json: JSON


--#METADATA#{}

declare class Object
end

declare class Instance
end

declare class ServiceProvider
end

declare class Enum
end

declare class EnumItem
end


--#METADATA#{"CREATABLE_INSTANCES":[], "SERVICES": []}
export type ExtraDataRelyingOnMutations = {
	RigType: Enum.HumanoidRigType,
}