local Packages = script.Parent.Parent.Parent

local Shared = require(Packages.Shared)
local LuauPolyfill = require(Packages.LuauPolyfill)

type Object = LuauPolyfill.Object
type Array<T> = LuauPolyfill.Array<T>

local Tag = Shared.Tag

local TagManagers = {}

local function setInitialTags(
	hostInstance: any,
	_tag: string,
	rawProps: Object,
	rootContainerElement: any
)
	for key, newValue in rawProps do
		if key == Tag then
			local rootTagManager = TagManagers[rootContainerElement]
			if rootTagManager == nil then
				rootTagManager = {}
				TagManagers[rootContainerElement] = rootTagManager
			end

			local tagSet = string.split(newValue or "", ",")

			for _, newTag in tagSet do
				local instancesForTag = rootTagManager[newTag]
				if instancesForTag == nil then
					instancesForTag = {}
					rootTagManager[newTag] = instancesForTag
				end
				table.insert(instancesForTag, hostInstance)
			end
		end
	end
end

local function updateTags(hostInstance: any, newProps: Object, lastProps: Object)
	for propKey, newValue in newProps do
		if propKey == Tag then
			local rootTagManager = TagManagers[hostInstance.rootContainerInstance]
			if rootTagManager == nil then
				rootTagManager = {}
				TagManagers[hostInstance.rootContainerInstance] = rootTagManager
			end

			local newTagSet = string.split(newValue or "", ",")
			local lastTagSet = string.split(lastProps[Tag] or "", ",")

			for _, lastTag in lastTagSet do
				local existingTagIndex = table.find(newTagSet, lastTag)
				if existingTagIndex == nil then
					local index = table.find(rootTagManager[lastTag], hostInstance)
					table.remove(rootTagManager[lastTag], index)
				else
					table.remove(newTagSet, existingTagIndex)
				end
			end

			for _, newTag in newTagSet do
				local instancesForTag = rootTagManager[newTag]
				if instancesForTag == nil then
					instancesForTag = {}
					rootTagManager[newTag] = instancesForTag
				end
				table.insert(instancesForTag, hostInstance)
			end
		end
	end
end

local function removeTags(hostInstance)
	for _, childInstance in hostInstance.children or {} do
		removeTags(childInstance)
	end

	local rootTagManager = TagManagers[hostInstance.rootContainerInstance]
	if rootTagManager == nil then
		return
	end

	local tagSet = string.split(hostInstance.props[Tag] or "", ",")
	for _, tag in tagSet do
		local instancesForTag = rootTagManager[tag]
		if instancesForTag ~= nil then
			local index = table.find(instancesForTag, hostInstance)
			table.remove(instancesForTag, index)
		end
	end
end

local function getInstancesForTag(rootContainerElement, tag)
	local rootTagManager = TagManagers[rootContainerElement] or {}
	return rootTagManager[tag] or {}
end

local exports = {
	setInitialTags = setInitialTags,
	updateTags = updateTags,
	removeTags = removeTags,
	getInstancesForTag = getInstancesForTag,
}

return exports


--!strict
--[[
	* Copyright (c) Roblox Corporation. All rights reserved.
	* Licensed under the MIT License (the "License");
	* you may not use this file except in compliance with the License.
	* You may obtain a copy of the License at
	*
	*     https://opensource.org/licenses/MIT
	*
	* Unless required by applicable law or agreed to in writing, software
	* distributed under the License is distributed on an "AS IS" BASIS,
	* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	* See the License for the specific language governing permissions and
	* limitations under the License.
]]

local Packages = script.Parent.Parent

local ReactGlobals = require(Packages.ReactGlobals)
local LuauPolyfill = require(Packages.LuauPolyfill)
local ReactSymbols = require(Packages.Shared).ReactSymbols

local ReactTypes = require(Packages.Shared)
type Binding<T> = ReactTypes.ReactBinding<T>
type BindingUpdater<T> = ReactTypes.ReactBindingUpdater<T>

local Symbol = LuauPolyfill.Symbol
local createSignal = require(script.Parent["createSignal.roblox"])

local BindingImpl = Symbol("BindingImpl")

type BindingInternal<T> = {
	["$$typeof"]: typeof(ReactSymbols.REACT_BINDING_TYPE),
	value: T,

	getValue: (BindingInternal<T>) -> T,
	-- FIXME Luau: can't define recursive types with different parameters
	map: <U>(BindingInternal<T>, (T) -> U) -> any,

	update: (T) -> (),
	subscribe: ((T) -> ()) -> () -> (),
}

local BindingInternalApi = {}

local bindingPrototype = {}

function bindingPrototype.getValue<T>(binding: BindingInternal<T>): T
	return BindingInternalApi.getValue(binding)
end

function bindingPrototype.map<T, U>(
	binding: BindingInternal<T>,
	predicate: (T) -> U
): Binding<U>
	return BindingInternalApi.map(binding, predicate)
end

local BindingPublicMeta = {
	__index = bindingPrototype,
	__tostring = function(self)
		return string.format("RoactBinding(%s)", tostring(self:getValue()))
	end,
}

function BindingInternalApi.update<T>(binding: any, newValue: T)
	return (binding[BindingImpl] :: BindingInternal<T>).update(newValue)
end

function BindingInternalApi.subscribe<T>(binding: any, callback: (T) -> ())
	return (binding[BindingImpl] :: BindingInternal<T>).subscribe(callback)
end

function BindingInternalApi.getValue<T>(binding: any): T
	return (binding[BindingImpl] :: BindingInternal<T>):getValue()
end

function BindingInternalApi.create<T>(initialValue: T): (Binding<T>, BindingUpdater<T>)
	local subscribe, fire = createSignal()
	local impl = {
		value = initialValue,
		subscribe = subscribe,
	}

	function impl.update(newValue: T)
		impl.value = newValue
		fire(newValue)
	end

	function impl.getValue()
		return impl.value
	end

	local source
	if ReactGlobals.__DEV__ then
		-- ROBLOX TODO: LUAFDN-619 - improve debug stacktraces for bindings
		source = debug.traceback("Binding created at:", 3)
	end

	return (setmetatable({
		["$$typeof"] = ReactSymbols.REACT_BINDING_TYPE,
		[BindingImpl] = impl,
		_source = source,
	}, BindingPublicMeta) :: any) :: Binding<T>,
		impl.update
end

function BindingInternalApi.map<T, U>(
	upstreamBinding: BindingInternal<T>,
	predicate: (T) -> U
): Binding<U>
	if ReactGlobals.__DEV__ then
		-- ROBLOX TODO: More informative error messages here
		assert(
			typeof(upstreamBinding) == "table"
				and upstreamBinding["$$typeof"] == ReactSymbols.REACT_BINDING_TYPE,
			"Expected `self` to be a binding"
		)
		assert(typeof(predicate) == "function", "Expected arg #1 to be a function")
	end

	local impl = {}

	function impl.subscribe(callback)
		return BindingInternalApi.subscribe(upstreamBinding, function(newValue)
			callback(predicate(newValue))
		end)
	end

	function impl.update(newValue)
		error("Bindings created by Binding:map(fn) cannot be updated directly", 2)
	end

	function impl.getValue()
		return predicate(upstreamBinding:getValue())
	end

	local source
	if ReactGlobals.__DEV__ then
		-- ROBLOX TODO: LUAFDN-619 - improve debug stacktraces for bindings
		source = debug.traceback("Mapped binding created at:", 3)
	end

	return (
		setmetatable({
			["$$typeof"] = ReactSymbols.REACT_BINDING_TYPE,
			[BindingImpl] = impl,
			_source = source,
		}, BindingPublicMeta) :: any
	) :: Binding<U>
end

-- The `join` API is used statically, so the input will be a table with values
-- typed as the public Binding type
function BindingInternalApi.join<T>(
	upstreamBindings: { [string | number]: Binding<any> }
): Binding<T>
	if ReactGlobals.__DEV__ then
		assert(typeof(upstreamBindings) == "table", "Expected arg #1 to be of type table")

		for key, value in upstreamBindings do
			if
				typeof(value) ~= "table"
				or (value :: any)["$$typeof"] ~= ReactSymbols.REACT_BINDING_TYPE
			then
				local message = ("Expected arg #1 to contain only bindings, but key %q had a non-binding value"):format(
					tostring(key)
				)
				error(message, 2)
			end
		end
	end

	local impl = {}

	local function getValue()
		local value = {}

		-- ROBLOX FIXME Luau: needs CLI-56711 resolved to eliminate ipairs()
		for key, upstream in pairs(upstreamBindings) do
			value[key] = upstream:getValue()
		end

		return value
	end

	function impl.subscribe(callback)
		-- ROBLOX FIXME: type refinements
		local disconnects: any = {}

		for key, upstream in upstreamBindings do
			disconnects[key] = BindingInternalApi.subscribe(upstream, function(newValue)
				callback(getValue())
			end)
		end

		return function()
			if disconnects == nil then
				return
			end

			for _, disconnect in disconnects do
				disconnect()
			end

			disconnects = nil
		end
	end

	function impl.update(newValue)
		error("Bindings created by joinBindings(...) cannot be updated directly", 2)
	end

	function impl.getValue()
		return getValue()
	end

	local source
	if ReactGlobals.__DEV__ then
		-- ROBLOX TODO: LUAFDN-619 - improve debug stacktraces for bindings
		source = debug.traceback("Joined binding created at:", 2)
	end

	return (
		setmetatable({
			["$$typeof"] = ReactSymbols.REACT_BINDING_TYPE,
			[BindingImpl] = impl,
			_source = source,
		}, BindingPublicMeta) :: any
	) :: Binding<T>
end

return BindingInternalApi


--!strict
--[[
	* Copyright (c) Roblox Corporation. All rights reserved.
	* Licensed under the MIT License (the "License");
	* you may not use this file except in compliance with the License.
	* You may obtain a copy of the License at
	*
	*     https://opensource.org/licenses/MIT
	*
	* Unless required by applicable law or agreed to in writing, software
	* distributed under the License is distributed on an "AS IS" BASIS,
	* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	* See the License for the specific language governing permissions and
	* limitations under the License.
]]

local Packages = script.Parent.Parent
local ReactRoblox = require(Packages.ReactRoblox)
local ReactGlobals = require(Packages.ReactGlobals)

local warnOnce = require(script.Parent.warnOnce)

local function PortalComponent(props)
	if ReactGlobals.__DEV__ and ReactGlobals.__COMPAT_WARNINGS__ then
		warnOnce("Roact.Portal", "Please use the createPortal API on ReactRoblox instead")
	end
	return ReactRoblox.createPortal(props.children, props.target)
end

return PortalComponent


--!strict
--[[
	* Copyright (c) Roblox Corporation. All rights reserved.
	* Licensed under the MIT License (the "License");
	* you may not use this file except in compliance with the License.
	* You may obtain a copy of the License at
	*
	*     https://opensource.org/licenses/MIT
	*
	* Unless required by applicable law or agreed to in writing, software
	* distributed under the License is distributed on an "AS IS" BASIS,
	* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	* See the License for the specific language governing permissions and
	* limitations under the License.
]]
local Packages = script.Parent.Parent
local ReactGlobals = require(Packages.ReactGlobals)
local LuauPolyfill = require(Packages.LuauPolyfill)
local inspect = LuauPolyfill.util.inspect
local ReactRoblox = require(Packages.ReactRoblox)
type RootType = ReactRoblox.RootType

local warnOnce = require(script.Parent.warnOnce)

type RoactHandle = {
	root: RootType,
	key: string | number,
	parent: any, -- ROBLOX TODO: Instance?
}

local function mount(element: any, parent: any, key: string?): RoactHandle
	if ReactGlobals.__DEV__ and ReactGlobals.__COMPAT_WARNINGS__ then
		warnOnce("mount", "Please use the createRoot API in ReactRoblox")
	end

	if parent ~= nil and typeof(parent) ~= "Instance" then
		error(
			string.format(
				"Cannot mount element (`%s`) into a parent that is not a Roblox Instance (got type `%s`) \n%s",
				(function()
					if element then
						return tostring(element.type)
					end
					return "<unknown>"
				end)(),
				typeof(parent),
				(function()
					if parent ~= nil then
						return inspect(parent)
					end
					return ""
				end)()
			)
		)
	end

	-- Since we use portals to actually parent to the provided parent argument,
	-- the container instance that we provide to createRoot is just a
	-- dummy instance.
	local root
	if _G.__ROACT_17_COMPAT_LEGACY_ROOT__ then
		root = ReactRoblox.createLegacyRoot(Instance.new("Folder"))
	else
		root = ReactRoblox.createRoot(Instance.new("Folder"))
	end
	if parent == nil then
		local newParent = Instance.new("Folder")
		newParent.Name = "Target"
		parent = newParent
	end
	if key == nil then
		if _G.__ROACT_17_COMPAT_LEGACY_ROOT__ then
			key = "ReactLegacyRoot"
		else
			key = "ReactRoot"
		end
	end

	-- ROBLOX TODO: remove INLINE_ACT flag when all tests are updated to use
	-- `act` explicitly
	if ReactGlobals.__ROACT_17_INLINE_ACT__ then
		ReactRoblox.act(function()
			root:render(ReactRoblox.createPortal({ [key] = element }, parent))
		end)
	else
		root:render(ReactRoblox.createPortal({ [key] = element }, parent))
	end

	return {
		root = root,
		-- To preserve the same key and portal to the same parent on update, we
		-- need to stash them in the opaque "tree" reference returned by `mount`
		parent = parent,
		key = key :: string,
	}
end

local function update(roactHandle: RoactHandle, element)
	if ReactGlobals.__DEV__ and ReactGlobals.__COMPAT_WARNINGS__ then
		warnOnce("update", "Please use the createRoot API in ReactRoblox")
	end

	local key = roactHandle.key
	local parent = roactHandle.parent
	-- ROBLOX TODO: remove INLINE_ACT flag when all tests are updated to use
	-- `act` explicitly
	if ReactGlobals.__ROACT_17_INLINE_ACT__ then
		ReactRoblox.act(function()
			roactHandle.root:render(
				ReactRoblox.createPortal({ [key :: string] = element }, parent)
			)
		end)
	else
		roactHandle.root:render(
			ReactRoblox.createPortal({ [key :: string] = element }, parent)
		)
	end

	return roactHandle
end

local function unmount(roactHandle: RoactHandle)
	if ReactGlobals.__DEV__ and ReactGlobals.__COMPAT_WARNINGS__ then
		warnOnce("unmount", "Please use the createRoot API in ReactRoblox")
	end

	-- ROBLOX TODO: remove INLINE_ACT flag when all tests are updated to use
	-- `act` explicitly
	if ReactGlobals.__ROACT_17_INLINE_ACT__ then
		ReactRoblox.act(function()
			roactHandle.root:unmount()
		end)
	else
		roactHandle.root:unmount()
	end
end

return {
	mount = mount,
	update = update,
	unmount = unmount,
}


--!strict
--[[
	* Copyright (c) Roblox Corporation. All rights reserved.
	* Licensed under the MIT License (the "License");
	* you may not use this file except in compliance with the License.
	* You may obtain a copy of the License at
	*
	*     https://opensource.org/licenses/MIT
	*
	* Unless required by applicable law or agreed to in writing, software
	* distributed under the License is distributed on an "AS IS" BASIS,
	* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	* See the License for the specific language governing permissions and
	* limitations under the License.
]]
local Packages = script.Parent.Parent
local ReactGlobals = require(Packages.ReactGlobals)
local React = require(Packages.React)

local warnOnce = require(script.Parent.warnOnce)

return function(elements)
	if ReactGlobals.__DEV__ and ReactGlobals.__COMPAT_WARNINGS__ then
		warnOnce(
			"createFragment",
			"Please instead use:\n\tReact.createElement(React.Fragment, ...)"
		)
	end
	return React.createElement(React.Fragment, nil, elements)
end


--!strict
--[[
	* Copyright (c) Roblox Corporation. All rights reserved.
	* Licensed under the MIT License (the "License");
	* you may not use this file except in compliance with the License.
	* You may obtain a copy of the License at
	*
	*     https://opensource.org/licenses/MIT
	*
	* Unless required by applicable law or agreed to in writing, software
	* distributed under the License is distributed on an "AS IS" BASIS,
	* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	* See the License for the specific language governing permissions and
	* limitations under the License.
]]
local Packages = script.Parent

local React = require(Packages.React)
local ReactRoblox = require(Packages.ReactRoblox)
local Shared = require(Packages.Shared)

local RoactTree = require(script.RoactTree)

return {
	-- Overlapping contents of public `React` interface
	Component = React.Component,
	PureComponent = React.PureComponent,
	createElement = React.createElement,
	createRef = React.createRef,
	forwardRef = React.forwardRef,
	createContext = React.createContext,
	-- This public interface is aligned but is a deviation in React. It's
	-- necessary to accommodate lua table semantics when merging state
	None = React.None,

	-- Compatibility layer for top-level interface, stands in for `createRoot`
	-- and similar APIs
	mount = RoactTree.mount,
	update = RoactTree.update,
	unmount = RoactTree.unmount,

	-- API compatibility layers to accommodate old interfaces
	createFragment = require(script.createFragment),
	oneChild = require(script.oneChild),
	setGlobalConfig = require(script.setGlobalConfig),
	Portal = require(script.Portal),

	-- Compatibility layer for special symbol keys which will be replaced with
	-- reserved prop names in Roact 17
	Ref = "ref",
	-- FIXME: React actually _does_ have a `.Children`, which has different
	-- behavior (none of which overlaps with Roact). Should we keep this as is
	-- and accept that React.Children and RoactCompat.Children will be totally
	-- different things?
	Children = "children",

	-- Event/Change keys for Roact-specific event subscription behavior
	Event = Shared.Event,
	Change = Shared.Change,

	-- Binding interface entry-point for Roact-specific 'bindings' feature
	createBinding = React.createBinding,
	joinBindings = React.joinBindings,

	act = ReactRoblox.act,
}


--!strict
--[[
	* Copyright (c) Roblox Corporation. All rights reserved.
	* Licensed under the MIT License (the "License");
	* you may not use this file except in compliance with the License.
	* You may obtain a copy of the License at
	*
	*     https://opensource.org/licenses/MIT
	*
	* Unless required by applicable law or agreed to in writing, software
	* distributed under the License is distributed on an "AS IS" BASIS,
	* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	* See the License for the specific language governing permissions and
	* limitations under the License.
]]

local Packages = script.Parent.Parent
local ReactGlobals = require(Packages.ReactGlobals)
local warnOnce = require(script.Parent.warnOnce)

return function(_config)
	if ReactGlobals.__DEV__ and ReactGlobals.__COMPAT_WARNINGS__ then
		warnOnce(
			"setGlobalConfig",
			"Roact 17 uses a `ReactGlobals.__DEV__` flag to enable development behavior. "
				.. "If you're seeing this warning, you already have it enabled. "
				.. "Please remove any redundant uses of `setGlobalConfig`."
		)
	end
	-- No equivalent behavior can be applied here
end


--!strict
--[[
	* Copyright (c) Roblox Corporation. All rights reserved.
	* Licensed under the MIT License (the "License");
	* you may not use this file except in compliance with the License.
	* You may obtain a copy of the License at
	*
	*     https://opensource.org/licenses/MIT
	*
	* Unless required by applicable law or agreed to in writing, software
	* distributed under the License is distributed on an "AS IS" BASIS,
	* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	* See the License for the specific language governing permissions and
	* limitations under the License.
]]

local Packages = script.Parent.Parent
local console = require(Packages.Shared).console

local warnedAbout = {}

local function warnOnce(name: string, message: string)
	if not warnedAbout[name] then
		console.warn(
			"The legacy Roact API '%s' is deprecated, and will be removed "
				.. "in a future release.\n\n%s",
			name,
			message
		)
	end
	warnedAbout[name] = true
end

return warnOnce


-- ROBLOX note: no upstream
--!strict
--[[
	* Copyright (c) Roblox Corporation. All rights reserved.
	* Licensed under the MIT License (the "License");
	* you may not use this file except in compliance with the License.
	* You may obtain a copy of the License at
	*
	*     https://opensource.org/licenses/MIT
	*
	* Unless required by applicable law or agreed to in writing, software
	* distributed under the License is distributed on an "AS IS" BASIS,
	* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	* See the License for the specific language governing permissions and
	* limitations under the License.
]]

local YIELD_ERROR = [[

Yielding is not allowed inside components or hooks.

Yielding in a component stalls React's scheduler, freezing the entire application until the yield completes.
Check the stack trace below to find the exact location of the yield.

For more details and how to fix this, see: go/react-yield-error
]]

local function resultHandler(co: thread, ok: boolean, ...)
	if not ok then
		local err = (...)
		if typeof(err) == "string" then
			error(debug.traceback(co, err), 3)
		else
			-- If the error is not of type string, just assume it has some
			-- meaningful information and rethrow it so that top-level error
			-- handlers can process it.
			error(err, 3)
		end
	end

	if coroutine.status(co) ~= "dead" then
		error(debug.traceback(co, YIELD_ERROR), 3)
	end

	return ...
end

--[[
    Prevents a callback from yielding. If the callback yields, an error will be
    thrown.
]]
local function NoYield<A..., R...>(callback: (A...) -> R..., ...: A...): R...
	local co = coroutine.create(callback)
	return resultHandler(co, coroutine.resume(co, ...))
end

return NoYield


--!strict
--[[*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
*]]
local Tracing = require(script.Parent.Tracing)
local TracingSubscriptions = require(script.Parent.TracingSubscriptions)
-- ROBLOX deviation: export Tracing type from the package exports to avoid direct file access
export type Interaction = Tracing.Interaction

local initializeScheduler = require(script.Parent.Scheduler)
local HostConfig = require(script.Parent.forks["SchedulerHostConfig.mock"])

local Scheduler = initializeScheduler(HostConfig)

local exports = {}
exports.tracing = {}
-- ROBLOX FIXME Luau: need to fix CLI-56768 to remove any casts
for key, value in Scheduler :: any do
	exports[key] = value
end
for key, value in Tracing :: any do
	exports.tracing[key] = value
end
for key, value in TracingSubscriptions :: any do
	exports.tracing[key] = value
end

exports.unstable_flushAllWithoutAsserting = HostConfig.unstable_flushAllWithoutAsserting
exports.unstable_flushNumberOfYields = HostConfig.unstable_flushNumberOfYields
exports.unstable_flushExpired = HostConfig.unstable_flushExpired
exports.unstable_clearYields = HostConfig.unstable_clearYields
exports.unstable_flushUntilNextPaint = HostConfig.unstable_flushUntilNextPaint
exports.unstable_flushAll = HostConfig.unstable_flushAll
exports.unstable_yieldValue = HostConfig.unstable_yieldValue
exports.unstable_advanceTime = HostConfig.unstable_advanceTime
exports.unstable_Profiling = Scheduler.unstable_Profiling

return exports


--!strict
--[[
	* Copyright (c) Roblox Corporation. All rights reserved.
	* Licensed under the MIT License (the "License");
	* you may not use this file except in compliance with the License.
	* You may obtain a copy of the License at
	*
	*     https://opensource.org/licenses/MIT
	*
	* Unless required by applicable law or agreed to in writing, software
	* distributed under the License is distributed on an "AS IS" BASIS,
	* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	* See the License for the specific language governing permissions and
	* limitations under the License.
]]

local Packages = script.Parent.Parent
local LuauPolyfill = require(Packages.LuauPolyfill)
type Error = LuauPolyfill.Error
type Object = LuauPolyfill.Object
type Array<T> = LuauPolyfill.Array<T>
local Error = LuauPolyfill.Error
local inspect = LuauPolyfill.util.inspect

local ReactFeatureFlags = require(script.Parent.ReactFeatureFlags)
local filterInternalStackFrames = ReactFeatureFlags.filterInternalStackFrames

local DIVIDER = "\n------ Error caught by React ------\n"

-- List of known React package names that should be filtered from stacks.
-- Note that this isn't an exhaustive list of all packages, only the ones that
-- are likely to appear in a stack frame.
local REACT_PACKAGE_NAMES = {
	"React",
	"ReactDevtoolsShared",
	"ReactNoopRenderer",
	"ReactReconciler",
	"ReactRefresh",
	"ReactRoblox",
	"RoactCompat",
	"Scheduler",
	"Shared",
}

-- Cache React package path prefixes (lazily initialized)
local reactPackagePrefixes: { string }? = nil

--[[
	Build a list of full instance paths for all React packages.
	These are siblings under the Packages folder.
]]
local function getReactPackagePrefixes(): { string }
	if reactPackagePrefixes then
		return reactPackagePrefixes
	end

	local reactPackagePrefixes_ = {}

	for _, packageName in REACT_PACKAGE_NAMES do
		-- Note, we check the parent of Packages because we're interested in the
		-- package index, not the linkers for Shared.
		local package = Packages.Parent:FindFirstChild(packageName)
		if package then
			-- Get full instance path and remove "game." prefix if present
			local packagePath = package:GetFullName():gsub("^game%.", "")
			table.insert(reactPackagePrefixes_, packagePath)
		end
	end

	reactPackagePrefixes = reactPackagePrefixes_
	return reactPackagePrefixes_
end

--[[
	Try to determine if a stack frame originates from inside React by checking
	if the source path starts with any known React package path in the instance
	tree.
]]
local function isInternalFrame(source: string): boolean
	local prefixes = getReactPackagePrefixes()

	for _, prefix in prefixes do
		if string.sub(source, 1, #prefix) == prefix then
			return true
		end
	end

	return false
end

--[[
	Build a stack string starting at the specified call stack level, skipping
	any internal React frames. Mirrors the format of `debug.traceback()`.

	If the first frame is internal to React, no filtering is applied - this
	indicates the error originated from within React itself.
]]
local function buildStackString(level: number): string
	local stack = ""

	local handledFirstSource = false
	local shouldFilter = false

	for i = level + 1, math.huge do
		local source, line, fnName = debug.info(i, "sln")
		if not source then
			break
		end

		if source == "[C]" then
			-- Skip internal C frames
			continue
		end

		if not handledFirstSource then
			-- Decide whether to filter based on the first non-C frame
			shouldFilter = not isInternalFrame(source)
			handledFirstSource = true
		end

		if shouldFilter and isInternalFrame(source) then
			continue
		end

		stack ..= `{source}:{line} function {fnName or "?"}\n`
	end

	-- Remove trailing newline
	stack = string.gsub(stack, "\n$", "")

	return stack
end

--[[
	React does a lot of catching, retrying, and rethrowing errors that would
	typically result in loss of meaningful stack information.

	We use xpcall combined with this error function to capture and rethrow in a
	way that retains some stack information.
]]
local function describeError(e: string | Error): Error
	if typeof(e) == "string" then
		local _, endOfStackFrame = string.find(e, ":[%d]+: ")
		local message = if endOfStackFrame then string.sub(e, endOfStackFrame + 1) else e

		local err = LuauPolyfill.Error.new(message)
		if filterInternalStackFrames then
			err.stack = buildStackString(2)
		else
			err.stack = debug.traceback(nil, 2)
		end
		return err
	end
	return e :: Error
end

--[[
	Even though Luau can catch and rethrow arbitrary objects, only string errors
	are supported by the ScriptContext.ErrorDetailed signal (the mechanism used
	to catch unhandled errors at the top level).

	This function turns an arbitrary error object into a detailed string message
	to avoid any loss of information.
]]
local function errorToString(error_: Error | Object | string | Array<any>): string
	local errorString
	if typeof(error_) == "table" then
		if (error_ :: Error).message and (error_ :: Error).stack then
			-- Adding these clear dividers helps us split this error back up
			-- into pieces later. We include one at the beginning so that the
			-- final stack frame added by rethrowing can be carved off
			errorString = DIVIDER
				.. (error_ :: Error).message
				.. DIVIDER
				.. tostring((error_ :: Error).stack)
		else
			errorString = inspect(error_)
		end
	else
		errorString = inspect(error_)
	end

	return errorString
end

--[[
	If an error string was generated from an Error object via the errorToString
	function above, it can be easily split back out into an informative error
	object.
]]
local function parseReactError(error_: string): (Error, string)
	local split = string.split(error_, DIVIDER)

	if #split == 3 then
		local rethrow, message, stack = table.unpack(split)

		local newError = Error.new(message)
		newError.stack = stack

		return newError, rethrow
	else
		-- This error was not in the expected format, so we use the whole string
		-- as the 'message' value and nil out the stack (it would be misleading
		-- if we included the one generated here)
		local newError = Error.new(error_)
		newError.stack = nil

		return newError, ""
	end
end

return {
	describeError = describeError,
	errorToString = errorToString,
	parseReactError = parseReactError,
	-- Used only by tests
	__ERROR_DIVIDER = DIVIDER,
}


--[[
	* Copyright (c) Roblox Corporation. All rights reserved.
	* Licensed under the MIT License (the "License");
	* you may not use this file except in compliance with the License.
	* You may obtain a copy of the License at
	*
	*     https://opensource.org/licenses/MIT
	*
	* Unless required by applicable law or agreed to in writing, software
	* distributed under the License is distributed on an "AS IS" BASIS,
	* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	* See the License for the specific language governing permissions and
	* limitations under the License.
]]
--[[
	Change is used to generate special prop keys that can be used to connect to
	GetPropertyChangedSignal.

	Generally, Change is indexed by a Roblox property name:

		Roact.createElement("TextBox", {
			[Roact.Change.Text] = function(rbx)
				print("The TextBox", rbx, "changed text to", rbx.Text)
			end,
		})
]]

local Type = require(script.Parent.Parent["Type.roblox"])

local Change = {}

local changeMetatable = {
	__tostring = function(self)
		return string.format("RoactHostChangeEvent(%s)", self.name)
	end,
}

setmetatable(Change, {
	__index = function(self, propertyName)
		local changeListener = {
			[Type] = Type.HostChangeEvent,
			name = propertyName,
		}

		setmetatable(changeListener, changeMetatable)
		Change[propertyName] = changeListener

		return changeListener
	end,
})

return Change


--[[
	* Copyright (c) Roblox Corporation. All rights reserved.
	* Licensed under the MIT License (the "License");
	* you may not use this file except in compliance with the License.
	* You may obtain a copy of the License at
	*
	*     https://opensource.org/licenses/MIT
	*
	* Unless required by applicable law or agreed to in writing, software
	* distributed under the License is distributed on an "AS IS" BASIS,
	* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	* See the License for the specific language governing permissions and
	* limitations under the License.
]]
--[[
	Index into `Event` to get a prop key for attaching to an event on a Roblox
	Instance.

	Example:

		Roact.createElement("TextButton", {
			Text = "Hello, world!",

			[Roact.Event.MouseButton1Click] = function(rbx)
				print("Clicked", rbx)
			end
		})
]]

local Type = require(script.Parent.Parent["Type.roblox"])

local Event = {}

local eventMetatable = {
	__tostring = function(self)
		return string.format("RoactHostEvent(%s)", self.name)
	end,
}

setmetatable(Event, {
	__index = function(self, eventName)
		local event = {
			[Type] = Type.HostEvent,
			name = eventName,
		}

		setmetatable(event, eventMetatable)

		Event[eventName] = event

		return event
	end,
})

return Event


--[[
	* Copyright (c) Roblox Corporation. All rights reserved.
	* Licensed under the MIT License (the "License");
	* you may not use this file except in compliance with the License.
	* You may obtain a copy of the License at
	*
	*     https://opensource.org/licenses/MIT
	*
	* Unless required by applicable law or agreed to in writing, software
	* distributed under the License is distributed on an "AS IS" BASIS,
	* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	* See the License for the specific language governing permissions and
	* limitations under the License.
]]
--[[
	Special value for assigning tags to roblox instances via Roact
]]
local Symbol = require(script.Parent.Parent["Symbol.roblox"])

local Tag = Symbol.named("RobloxTag")

return Tag


--!strict
--[[*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
]]
local Packages = script.Parent.Parent
local LuauPolyfill = require(Packages.LuauPolyfill)
type Object = LuauPolyfill.Object

local flowtypes = require(script.Parent["flowtypes.roblox"])
type React_Element<ElementType> = flowtypes.React_Element<ElementType>
type React_StatelessFunctionalComponent<P> = flowtypes.React_StatelessFunctionalComponent<
	P
>
type React_ComponentType<P> = flowtypes.React_ComponentType<P>

export type Source = {
	fileName: string,
	lineNumber: number,
}
type Key = string | number
-- ROBLOX deviation: we're using the TypeScript definition here, which is more strict
export type ReactElement<P = Object, T = any> = {
	["$$typeof"]: number,

	-- ROBLOX FIXME Luau: Luau has some trouble and inlining the type param from createElement doesn't help
	type: React_StatelessFunctionalComponent<P> | React_ComponentType<P> | string,
	-- type: T,
	key: Key | nil,
	ref: any,
	props: P,

	-- ROBLOX deviation: upstream has this as interface, which is extensible, Luau types are closed by default
	-- ReactFiber
	_owner: any,

	-- __DEV__
	_store: any?,
	_self: React_Element<any>?,
	_shadowChildren: any?,
	_source: Source?,
}

-- deviation: Return something so that the module system is happy
return {}


--[[
	* Copyright (c) Roblox Corporation. All rights reserved.
	* Licensed under the MIT License (the "License");
	* you may not use this file except in compliance with the License.
	* You may obtain a copy of the License at
	*
	*     https://opensource.org/licenses/MIT
	*
	* Unless required by applicable law or agreed to in writing, software
	* distributed under the License is distributed on an "AS IS" BASIS,
	* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	* See the License for the specific language governing permissions and
	* limitations under the License.
]]
--[[
	ROBLOX deviation: ReactFiberHostConfig captures singleton state across the
	whole workspace. This file and the modules it requires were moved from React
	to untangle a cyclic workspace member dependency.

	Before:
	* ReactFiberHostConfig (and the 5 associated modules) lived in React
	* React had a dependency on Shared
	* Shared reached into React source to re-export ReactFiberHostConfig (cycle)

	After:
	* ReactFiberHostConfig (and the 5 associated modules) live in Shared
	* React depends on Shared
	* Shared has no intra-workspace dependencies (no cycles)
]]

-- types that are common across ReactFiberHostConfig files, moved here to avoid circular deps
type Object = { [string]: any }
export type OpaqueIDType = string | Object

return {
	WithNoHydration = require(script.WithNoHydration),
	WithNoPersistence = require(script.WithNoPersistence),
	WithNoTestSelectors = require(script.WithNoTestSelectors),
}


--!strict
--[[*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
]]

local Packages = script.Parent.Parent
local LuauPolyfill = require(Packages.LuauPolyfill)
type Array<T> = LuauPolyfill.Array<T>
type Object = LuauPolyfill.Object
type void = nil
type NonMaybeType<T> = T

local flowtypes = require(script.Parent["flowtypes.roblox"])
type React_Element<T> = flowtypes.React_Element<T>
type React_Node = flowtypes.React_Node
type SimpleMap<K, V> = { [K]: V }
type Iterable<T> = SimpleMap<string | number, T> | Array<T>

export type ReactNode<T = any> =
	React_Element<T>
	| ReactPortal
	-- | ReactText
	| ReactFragment
	| ReactProvider<T>
	| ReactConsumer<T>

export type ReactEmpty = nil | void | boolean

export type ReactFragment = ReactEmpty | Iterable<React_Node>

export type ReactNodeList = ReactEmpty | React_Node

-- ROBLOX deviation START: Roblox renderer doesn't support TextNode, only use of this type is in this file
-- export type ReactText = string | number;
-- ROBLOX deviation END
export type ReactProvider<T> = {
	["$$typeof"]: number,
	type: ReactProviderType<T>,
	key: nil | string,
	ref: nil,
	props: {
		value: T,
		children: ReactNodeList?,
		-- ROBLOX deviation START: only make this open to extension if absolutely necessary
		-- ...
		-- ROBLOX deviation END
	},
	-- ROBLOX deviation START: only make this open to extension if absolutely necessary
	-- ...
	-- ROBLOX deviation END
}

export type ReactProviderType<T> = {
	["$$typeof"]: number,
	_context: ReactContext<T>,
	-- ROBLOX deviation START: only make this open to extension if absolutely necessary
	-- ...
	-- ROBLOX deviation END
}

export type ReactConsumer<T> = {
	["$$typeof"]: number,
	type: ReactContext<T>, -- ROBLOX FIXME: Luau can't do <T> because:  Recursive type being used with different parameters
	key: nil | string,
	ref: nil,
	props: {
		children: (value: T) -> ReactNodeList,
		unstable_observedBits: number?,
		-- ROBLOX deviation START: only make this open to extension if absolutely necessary
		-- ...
		-- ROBLOX deviation END
	},
	-- ROBLOX deviation START: only make this open to extension if absolutely necessary
	-- ...
	-- ROBLOX deviation END
}

export type ReactContext<T> = {
	["$$typeof"]: number,
	Consumer: ReactContext<T>,
	Provider: ReactProviderType<T>,
	_calculateChangedBits: ((T, T) -> number)?,
	_currentValue: T,
	_currentValue2: T,
	_threadCount: number,
	-- DEV only
	_currentRenderer: Object | nil,
	_currentRenderer2: Object | nil,
	-- This value may be added by application code
	-- to improve DEV tooling display names
	displayName: string?,
	-- ROBLOX deviation START: only make this open to extension if absolutely necessary
	-- ...
	-- ROBLOX deviation END
}

export type ReactPortal = {
	["$$typeof"]: number,
	key: nil | string,
	containerInfo: any,
	children: ReactNodeList,
	-- TODO: figure out the API for cross-renderer implementation.
	implementation: any,
	-- ROBLOX deviation START: only make this open to extension if absolutely necessary
	-- ...
	-- ROBLOX deviation END
}

export type RefObject<T = any> = { current: T }

-- ROBLOX deviation START: No Luau support for numeric literal types
-- export type EventPriority = 0 | 1 | 2;
export type EventPriority = number
-- ROBLOX deviation END

local exports = {}
exports.DiscreteEvent = 0
exports.UserBlockingEvent = 1
exports.ContinuousEvent = 2

export type ReactFundamentalComponentInstance<C, H> = {
	currentFiber: Object,
	instance: any,
	prevProps: Object?,
	props: Object,
	impl: ReactFundamentalImpl<C, H>,
	state: Object,
}

export type ReactFundamentalImpl<C, H> = {
	displayName: string,
	reconcileChildren: boolean,
	getInitialState: nil | (Object) -> Object,
	getInstance: (C, Object, Object) -> H,
	getServerSideString: nil | (C, Object) -> string,
	getServerSideStringClose: nil | (C, Object) -> string,
	onMount: (C, any, Object, Object) -> (),
	shouldUpdate: nil | (C, Object?, Object, Object) -> boolean,
	onUpdate: nil | (C, any, Object?, Object, Object) -> (),
	onUnmount: nil | (C, any, Object, Object) -> (),
	onHydrate: nil | (C, Object, Object) -> boolean,
	onFocus: nil | (C, Object, Object) -> boolean,
}
export type ReactFundamentalComponent<C, H> = {
	["$$typeof"]: number,
	impl: ReactFundamentalImpl<C, H>,
}

export type ReactScope = {
	["$$typeof"]: number,
}

export type ReactScopeQuery = (
	type: string,
	-- ROBLOX deviation START: leave closed to extension unless necessary
	props: { [string]: any? },
	-- ROBLOX deviation END
	instance: any
) -> boolean

export type ReactScopeInstance = {
	DO_NOT_USE_queryAllNodes: (ReactScopeQuery) -> nil | Array<Object>,
	DO_NOT_USE_queryFirstNode: (ReactScopeQuery) -> nil | Object,
	containsNode: (Object) -> boolean,
	getChildContextValues: <T>(context: ReactContext<T>) -> Array<T>,
}

-- ROBLOX deviation START: Bindings are unique to Roact
-- FIXME Luau: can't create recursive type with different parameters, so we
-- need to split the generic `map` method into a different type and then
-- re-combine those types together
type CoreReactBinding<T> = {
	getValue: (self: CoreReactBinding<T>) -> T,
	_source: string?,
}
type ReactBindingMap = {
	map: <T, U>(
		self: CoreReactBinding<T> & ReactBindingMap,
		(T) -> U
	) -> ReactBindingMap & CoreReactBinding<U>,
}

export type ReactBinding<T> = CoreReactBinding<T> & ReactBindingMap
export type ReactBindingUpdater<T> = (T) -> ()
-- ROBLOX deviation END

-- Mutable source version can be anything (e.g. number, string, immutable data structure)
-- so long as it changes every time any part of the source changes.
export type MutableSourceVersion = NonMaybeType<any>

export type MutableSourceGetSnapshotFn<Source, Snapshot> = (source: Source) -> Snapshot

export type MutableSourceSubscribeFn<Source, Snapshot> = (
	source: Source,
	callback: (snapshot: Snapshot) -> ()
) -> () -> ()

export type MutableSourceGetVersionFn = (
	_source: NonMaybeType<any>
) -> MutableSourceVersion

export type MutableSource<Source> = {
	_source: Source,

	_getVersion: MutableSourceGetVersionFn,

	-- Tracks the version of this source at the time it was most recently read.
	-- Used to determine if a source is safe to read from before it has been subscribed to.
	-- Version number is only used during mount,
	-- since the mechanism for determining safety after subscription is expiration time.
	--
	-- As a workaround to support multiple concurrent renderers,
	-- we categorize some renderers as primary and others as secondary.
	-- We only expect there to be two concurrent renderers at most:
	-- React Native (primary) and Fabric (secondary);
	-- React DOM (primary) and React ART (secondary).
	-- Secondary renderers store their context values on separate fields.
	-- We use the same approach for Context.
	_workInProgressVersionPrimary: nil | MutableSourceVersion,
	_workInProgressVersionSecondary: nil | MutableSourceVersion,

	-- DEV only
	-- Used to detect multiple renderers using the same mutable source.
	_currentPrimaryRenderer: Object | nil,
	_currentSecondaryRenderer: Object | nil,
}

-- The subset of a Thenable required by things thrown by Suspense.
-- This doesn't require a value to be passed to either handler.
export type Wakeable = {
	andThen: (
		self: Wakeable,
		onFulfill: () -> ...any,
		onReject: () -> ...any
		-- ROBLOX FIXME Luau: needs union type packs to parse () | Wakeable
	) -> nil | Wakeable,
	-- Special flag to opt out of tracing interactions across a Suspense boundary.
	__reactDoNotTraceInteractions: boolean?,
}

-- The subset of a Promise that React APIs rely on. This resolves a value.
-- This doesn't require a return value neither from the handler nor the
-- then function.
-- ROBLOX FIXME: workaround for Luau recursive type used with different parameters. delete this copy once that issue is resolved.
export type _Thenable<R> = {
	andThen: <U>(
		self: _Thenable<R>,
		onFulfill: (R) -> ...U,
		onReject: (error: any) -> ...U
	) -> (),
}

export type Thenable<R> = {
	andThen: <U>(
		self: Thenable<R>,
		onFulfill: (R) -> ...(_Thenable<U> | U),
		onReject: (error: any) -> ...(_Thenable<U> | U)
		-- ROBLOX FIXME Luau: need union type packs to parse () | Thenable<U>: CLI-49836
	) -> nil | _Thenable<U>,
}

return exports


--[[
	* Copyright (c) Roblox Corporation. All rights reserved.
	* Licensed under the MIT License (the "License");
	* you may not use this file except in compliance with the License.
	* You may obtain a copy of the License at
	*
	*     https://opensource.org/licenses/MIT
	*
	* Unless required by applicable law or agreed to in writing, software
	* distributed under the License is distributed on an "AS IS" BASIS,
	* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	* See the License for the specific language governing permissions and
	* limitations under the License.
]]
--[[
	Contains markers for annotating objects with types.

	To set the type of an object, use `Type` as a key and the actual marker as
	the value:

		local foo = {
			[Type] = Type.Foo,
		}
]]

local Symbol = require(script.Parent["Symbol.roblox"])

local Type = newproxy(true)

local TypeInternal = {}

local function addType(name)
	TypeInternal[name] = Symbol.named("Roact" .. name)
end

addType("HostChangeEvent")
addType("HostEvent")

function TypeInternal.of(value)
	if typeof(value) ~= "table" then
		return nil
	end

	return value[Type]
end

getmetatable(Type).__index = TypeInternal

getmetatable(Type).__tostring = function()
	return "RoactType"
end

return Type


--[[
	* Copyright (c) Roblox Corporation. All rights reserved.
	* Licensed under the MIT License (the "License");
	* you may not use this file except in compliance with the License.
	* You may obtain a copy of the License at
	*
	*     https://opensource.org/licenses/MIT
	*
	* Unless required by applicable law or agreed to in writing, software
	* distributed under the License is distributed on an "AS IS" BASIS,
	* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	* See the License for the specific language governing permissions and
	* limitations under the License.
]]
--!strict
local console = require(script.Parent.console)

local Packages = script.Parent.Parent
local ReactGlobals = require(Packages.ReactGlobals)

-- ROBLOX DEVIATION: Initialize state to a singleton that warns on access and errors on assignment
-- initial state singleton
local UninitializedState = {}

setmetatable(UninitializedState, {
	__index = function(table, key)
		if ReactGlobals.__DEV__ then
			console.warn(
				"Attempted to access uninitialized state. Use setState to initialize state"
			)
		end
		return nil
	end,
	__newindex = function(table, key)
		if ReactGlobals.__DEV__ then
			console.error(
				"Attempted to directly mutate state. Use setState to assign new values to state."
			)
		end
		return nil
	end,
	__tostring = function(self)
		return "<uninitialized component state>"
	end,
	__metatable = "UninitializedState",
})

return UninitializedState


--[[
	* Copyright (c) Roblox Corporation. All rights reserved.
	* Licensed under the MIT License (the "License");
	* you may not use this file except in compliance with the License.
	* You may obtain a copy of the License at
	*
	*     https://opensource.org/licenses/MIT
	*
	* Unless required by applicable law or agreed to in writing, software
	* distributed under the License is distributed on an "AS IS" BASIS,
	* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	* See the License for the specific language governing permissions and
	* limitations under the License.
]]

-- deviation: this lets us have the same functionality as in React, without
-- having something like Babel to inject a different implementation of
-- console.warn and console.error into the code
-- Instead of using `LuauPolyfill.console`, React internals should use this
-- wrapper to be able to use consoleWithStackDev in dev mode
local Shared = script.Parent
local Packages = Shared.Parent
local ReactGlobals = require(Packages.ReactGlobals)
local LuauPolyfill = require(Packages.LuauPolyfill)
local console = LuauPolyfill.console
local consoleWithStackDev = require(Shared.consoleWithStackDev)

if ReactGlobals.__DEV__ then
	local newConsole = setmetatable({
		warn = consoleWithStackDev.warn,
		error = consoleWithStackDev.error,
	}, {
		__index = console,
	})
	return newConsole
end

return console


--!strict
--[[*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 *
 ]]
local Packages = script.Parent.Parent
local LuauPolyfill = require(Packages.LuauPolyfill)
local setTimeout = LuauPolyfill.setTimeout

return function(task)
	-- deviation: Replace with setImmediate once we create an equivalent polyfill
	return setTimeout(task, 0)
end


--!strict
--[[
	* Copyright (c) Roblox Corporation. All rights reserved.
	* Licensed under the MIT License (the "License");
	* you may not use this file except in compliance with the License.
	* You may obtain a copy of the License at
	*
	*     https://opensource.org/licenses/MIT
	*
	* Unless required by applicable law or agreed to in writing, software
	* distributed under the License is distributed on an "AS IS" BASIS,
	* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	* See the License for the specific language governing permissions and
	* limitations under the License.
]]

-- ROBLOX deviation: Promote `shared` to an actual unpublished package with a
-- real interface instead of just a bag of loose source code
local Packages = script.Parent
local LuauPolyfill = require(Packages.LuauPolyfill)
type Object = LuauPolyfill.Object

local ReactTypes = require(script.ReactTypes)
local flowtypes = require(script["flowtypes.roblox"])
local ReactElementType = require(script.ReactElementType)
local ReactFiberHostConfig = require(script.ReactFiberHostConfig)
local ReactSharedInternals = require(script.ReactSharedInternals)
local ErrorHandling = require(script["ErrorHandling.roblox"])

-- Re-export all top-level public types
export type ReactEmpty = ReactTypes.ReactEmpty
export type ReactFragment = ReactTypes.ReactFragment
export type ReactNodeList = ReactTypes.ReactNodeList
export type ReactProviderType<T> = ReactTypes.ReactProviderType<T>
export type ReactConsumer<T> = ReactTypes.ReactConsumer<T>
export type ReactProvider<T> = ReactTypes.ReactProvider<T>
export type ReactContext<T> = ReactTypes.ReactContext<T>
export type ReactPortal = ReactTypes.ReactPortal
export type RefObject<T = any> = ReactTypes.RefObject<T>
export type EventPriority = ReactTypes.EventPriority
export type ReactFundamentalComponentInstance<C, H> =
	ReactTypes.ReactFundamentalComponentInstance<C, H>
export type ReactFundamentalImpl<C, H> = ReactTypes.ReactFundamentalImpl<C, H>
export type ReactFundamentalComponent<C, H> = ReactTypes.ReactFundamentalComponent<C, H>
export type ReactScope = ReactTypes.ReactScope
export type ReactScopeQuery = ReactTypes.ReactScopeQuery
export type ReactScopeInstance = ReactTypes.ReactScopeInstance
-- ROBLOX deviation START: Re-export bindings types
export type ReactBinding<T> = ReactTypes.ReactBinding<T>
export type ReactBindingUpdater<T> = ReactTypes.ReactBindingUpdater<T>
-- ROBLOX deviation END
export type MutableSourceVersion = ReactTypes.MutableSourceVersion
export type MutableSourceGetSnapshotFn<Source, Snapshot> =
	ReactTypes.MutableSourceGetSnapshotFn<Source, Snapshot>
export type MutableSourceSubscribeFn<Source, Snapshot> = ReactTypes.MutableSourceSubscribeFn<
	Source,
	Snapshot
>
export type MutableSourceGetVersionFn = ReactTypes.MutableSourceGetVersionFn
export type MutableSource<Source> = ReactTypes.MutableSource<Source>
export type Wakeable = ReactTypes.Wakeable
export type Thenable<R> = ReactTypes.Thenable<R>
export type Source = ReactElementType.Source
export type ReactElement<P = Object, T = any> = ReactElementType.ReactElement<P, T>
export type OpaqueIDType = ReactFiberHostConfig.OpaqueIDType
export type Dispatcher = ReactSharedInternals.Dispatcher

-- re-export flowtypes from here. I wonder if this should be a separate 'package'?
export type React_Ref<ElementType> = flowtypes.React_Ref<ElementType>
export type React_Context<T> = flowtypes.React_Context<T>
export type React_AbstractComponent<Config, Instance> = flowtypes.React_AbstractComponent<
	Config,
	Instance
>
export type React_ForwardRefComponent<Config, Instance> =
	flowtypes.React_ForwardRefComponent<Config, Instance>
export type React_MemoComponent<Config, T> = flowtypes.React_MemoComponent<Config, T>
export type React_ComponentType<Config> = flowtypes.React_ComponentType<Config>
export type React_PureComponent<Props, State = nil> = flowtypes.React_PureComponent<
	Props,
	State
>
export type React_Component<Props, State> = flowtypes.React_Component<Props, State>
export type React_ElementProps<ElementType> = flowtypes.React_ElementProps<ElementType>
export type React_StatelessFunctionalComponent<Props> =
	flowtypes.React_StatelessFunctionalComponent<Props>
export type React_Node = flowtypes.React_Node
export type React_Element<ElementType> = flowtypes.React_Element<ElementType>
export type React_ElementType = flowtypes.React_ElementType
export type React_ElementConfig<C> = flowtypes.React_ElementConfig<C>
export type React_ElementRef<C> = flowtypes.React_ElementRef<C>
export type React_Portal = flowtypes.React_Portal
export type React_Key = flowtypes.React_Key

return {
	checkPropTypes = require(script.checkPropTypes),
	console = require(script.console),
	ConsolePatchingDev = require(script["ConsolePatchingDev.roblox"]),
	consoleWithStackDev = require(script.consoleWithStackDev),
	enqueueTask = require(script["enqueueTask.roblox"]),
	ExecutionEnvironment = require(script.ExecutionEnvironment),
	formatProdErrorMessage = require(script.formatProdErrorMessage),
	getComponentName = require(script.getComponentName),
	invariant = require(script.invariant),
	invokeGuardedCallbackImpl = require(script.invokeGuardedCallbackImpl),
	isValidElementType = require(script.isValidElementType),
	objectIs = require(script.objectIs),
	ReactComponentStackFrame = require(script.ReactComponentStackFrame),
	ReactElementType = require(script.ReactElementType),
	ReactErrorUtils = require(script.ReactErrorUtils),
	ReactFeatureFlags = require(script.ReactFeatureFlags),
	ReactInstanceMap = require(script.ReactInstanceMap),
	-- ROBLOX deviation: Instead of re-exporting from here, Shared actually owns
	-- these files itself
	ReactSharedInternals = ReactSharedInternals,
	-- ROBLOX deviation: Instead of extracting these out of the reconciler and
	-- then re-injecting the host config _into_ the reconciler, export these
	-- from shared for easier reuse
	ReactFiberHostConfig = ReactFiberHostConfig,

	ReactSymbols = require(script.ReactSymbols),
	ReactVersion = require(script.ReactVersion),
	shallowEqual = require(script.shallowEqual),
	UninitializedState = require(script["UninitializedState.roblox"]),
	ReactTypes = ReactTypes,

	-- ROBLOX DEVIATION: export error-stack-preserving utilities for use in
	-- scheduler and reconciler, and parsing function for use in public API
	describeError = ErrorHandling.describeError,
	errorToString = ErrorHandling.errorToString,
	parseReactError = ErrorHandling.parseReactError,

	-- ROBLOX DEVIATION: export Symbol and Type from Shared
	Symbol = require(script["Symbol.roblox"]),
	Type = require(script["Type.roblox"]),

	-- ROBLOX DEVIATION: export propmarkers from Shared
	Change = require(script.PropMarkers.Change),
	Event = require(script.PropMarkers.Event),
	Tag = require(script.PropMarkers.Tag),
}


local luau = require("@lune/luau")
local fs = require("@lune/fs")

local lossless_source = fs.readFile("lossless/parse.luau")

local lossless_bytecode = luau.compile(lossless_source, {
	optimizationLevel = 2,
	coverageLevel = 0,
	debugLevel = 1,
})

local lossless_bytecode_parser = luau.load(lossless_bytecode, {
	codegenEnabled = false,
})()

local lossless_native_parser = luau.load(lossless_bytecode, {
	codegenEnabled = true,
})()

local bytecode = luau.compile([[
local parse, code = ...

local start = os.clock()
local result = parse(code)
local stop = os.clock()

return stop - start, result
]], {
	optimizationLevel = 2,
	coverageLevel = 0,
	debugLevel = 1,
})

local bytecode_runner = luau.load(bytecode, {
	codegenEnabled = false,
})

local native_runner = luau.load(bytecode, {
	codegenEnabled = true,
})

local output: { { string } } = {}

local function print_output()
	local longest_name = 0
	for _, row in output do
		longest_name = math.max(longest_name, #row[1])
	end

	local longest_err = 6
	for _, row in output do
		longest_err = math.max(longest_err, #row[5])
	end

	print("| Name"..(" "):rep(longest_name - 4).." | Size KB | Time ms | Speed KB/s | Result |")

	for _, row in output do
		print(`| {row[1]}{(" "):rep(longest_name - #row[1])} | {row[2]} KB | {row[3]} ms | {row[4]} KB/s | {row[5]}{(" "):rep(longest_err - #row[5])} |`)
	end
end

local function run(runner: (...any) -> ...any, luaup: any, source: string, name: string)
	local time: number, result: any = runner(luaup, source)

	table.insert(output, {
		name, ("%04d"):format(#source / 1000), ("%04d"):format(time * 1000), ("%05d"):format((#source / 1000) / time),
		if result.success then "OK" else `{result.error.span.start}:{result.error.span.finish} {result.error.message}`,
	})
end

print("Running benchmarks to warm up...")

run(bytecode_runner, lossless_bytecode_parser, fs.readFile("lib/lossless.luau"), "lossless.luau")

for _, name in fs.readDir("bench/cases") do
	run(bytecode_runner, lossless_bytecode_parser, fs.readFile(`bench/cases/{name}`), name)
end

run(native_runner, lossless_native_parser, fs.readFile("lib/lossless.luau"), "lossless.luau")

for _, name in fs.readDir("bench/cases") do
	run(native_runner, lossless_native_parser, fs.readFile(`bench/cases/{name}`), name)
end

output = {}

print("Running bytecode benchmarks...")

run(bytecode_runner, lossless_bytecode_parser, fs.readFile("lib/lossless.luau"), "lossless.luau")

for _, name in fs.readDir("bench/cases") do
	run(bytecode_runner, lossless_bytecode_parser, fs.readFile(`bench/cases/{name}`), name)
end

print_output()
output = {}

print("Running native benchmarks...")

run(native_runner, lossless_native_parser, fs.readFile("lib/lossless.luau"), "lossless.luau")

for _, name in fs.readDir("bench/cases") do
	run(native_runner, lossless_native_parser, fs.readFile(`bench/cases/{name}`), name)
end

print_output()
output = {}


local cst = require("./cst")

local function unreachable(_: never): never
	error("unreachable")
end

local span_generic_declaration: (cst.GenericDeclaration) -> cst.Span
local span_generic_declaration_with_defaults: (cst.GenericDeclarationWithDefaults) -> cst.Span
local span_type_arg: (cst.TypeArg) -> cst.Span
local span_table_type_field: (cst.TableTypeField) -> cst.Span
local span_tail_type: (cst.TailType) -> cst.Span
local span_type: (cst.Type) -> cst.Span
local span_type_pack: (cst.TypePack) -> cst.Span
local span_return_type: (cst.ReturnType) -> cst.Span
local span_binding: (cst.Binding) -> cst.Span
local span_istring: (cst.IString) -> cst.Span
local span_if_else_expr_branch: (cst.IfElseExprBranch) -> cst.Span
local span_expr: (cst.Expr) -> cst.Span
local span_function_arg: (cst.FunctionArg) -> cst.Span
local span_var_root: (cst.VarRoot) -> cst.Span
local span_var_suffix: (cst.VarSuffix) -> cst.Span
local span_var: (cst.Var) -> cst.Span
local span_table_field: (cst.TableField) -> cst.Span
local span_table: (cst.Table) -> cst.Span
local span_function_body: (cst.FunctionBody) -> cst.Span
local span_if_stat_branch: (cst.IfStatBranch) -> cst.Span
local span_stat: (cst.Stat) -> cst.Span
local span_last_stat: (cst.LastStat) -> cst.Span
local span_block: (cst.Block) -> cst.Span

local function merge(left: cst.Span, right: cst.Span): cst.Span
	return {
		x = left.x,
		y = right.y,
		z = 0,
	}
end

function span_generic_declaration(node: cst.GenericDeclaration): cst.Span
	local angles = node.angles
	return merge(angles.open.span, angles.close.span)
end

function span_generic_declaration_with_defaults(node: cst.GenericDeclarationWithDefaults): cst.Span
	local angles = node.angles
	return merge(angles.open.span, angles.close.span)
end

function span_type_arg(node: cst.TypeArg): cst.Span
	if
		node.kind == "variadic"
		or node.kind == "generic"
		or node.kind == "list"
	then
		return span_type_pack(node)
	else
		return span_type(node)
	end
end

function span_table_type_field(node: cst.TableTypeField): cst.Span
	local access = node.access

	if access == nil then
		local span: cst.Span

		if node.kind == "nameprop" then
			span = node.name.span
		elseif node.kind == "stringprop" or node.kind == "indexer" then
			span = node.bracks.open.span
		else
			unreachable(node.kind)
		end

		return merge(span, span_type(node.type))
	else
		return merge(access.span, span_type(node.type))
	end
end

function span_tail_type(node: cst.TailType): cst.Span
	return span_type_pack(node)
end

function span_type(node: cst.Type): cst.Span
	if
		node.kind == "nil"
		or node.kind == "boolean"
		or node.kind == "string"
	then
		return node.tok.span
	elseif node.kind == "reference" then
		local prefix = node.prefix
		local generics = node.generics

		return merge(
			if prefix then prefix.name.span else node.name.span,
			if generics then generics.angles.close.span else node.name.span
		)
	elseif node.kind == "typeof" then
		return merge(
			node.typeof_tok.span,
			node.parens.close.span
		)
	elseif node.kind == "array" or node.kind == "table" then
		return merge(
			node.braces.open.span,
			node.braces.close.span
		)
	elseif node.kind == "function" then
		return merge(
			node.parens.open.span,
			span_return_type(node.ret)
		)
	elseif node.kind == "paren" then
		node = node :: cst.Type_Paren -- Luau Bug
		local parens = node.parens

		return merge(
			parens.open.span,
			parens.close.span
		)
	elseif node.kind == "optional" then
		return merge(
			span_type(node.type),
			node.question.span
		)
	elseif node.kind == "union" or node.kind == "intersection" then
		node = node :: cst.Type_Union | cst.Type_Intersection -- Luau Bug

		local leading = node.leading
		local last = node.types[(#node.types :: any) :: number]

		return merge(
			if leading then leading.span else span_type(node.types[1].node),
			span_type(last.node)
		) :: cst.Span
	else
		return unreachable(node.kind)
	end
end

function span_type_pack(node: cst.TypePack): cst.Span
	if node.kind == "variadic" then
		return merge(
			node.dots.span,
			span_type(node.type)
		)
	elseif node.kind == "generic" then
		return merge(
			node.name.span,
			node.dots.span
		)
	elseif node.kind == "list" then
		local parens = node.parens
		return merge(
			parens.open.span,
			parens.close.span
		)
	else
		return unreachable(node.kind)
	end
end

function span_return_type(node: cst.ReturnType): cst.Span
	if
		node.kind == "variadic"
		or node.kind == "generic"
		or node.kind == "list"
	then
		return span_type_pack(node)
	else
		return span_type(node)
	end
end

function span_binding(node: cst.Binding): cst.Span
	local type = node.type
	if type then
		return merge(
			node.name.span,
			span_type(type.type)
		)
	else
		return node.name.span
	end
end

function span_istring(node: cst.IString): cst.Span
	if node.kind == "simple" then
		return node.tok.span
	elseif node.kind == "complex" then
		local first = node.first
		local last = node.last

		return merge(
			first.span,
			last.span
		)
	else
		return unreachable(node.kind)
	end
end

function span_if_else_expr_branch(node: cst.IfElseExprBranch): cst.Span
	return merge(
		span_expr(node.condition),
		span_expr(node.body)
	)
end

function span_expr(node: cst.Expr): cst.Span
	if
		node.kind == "nil"
		or node.kind == "boolean"
		or node.kind == "number"
		or node.kind == "string"
		or node.kind == "varargs"
	then
		return node.tok.span
	elseif node.kind == "istring" then
		return span_istring(node.istring)
	elseif node.kind == "table" then
		return span_table(node.table)
	elseif node.kind == "function" then
		local attribute = node.attributes[1]

		if attribute then
			return merge(
				attribute.span,
				span_function_body(node.body)
			)
		else
			return merge(
				node.function_tok.span,
				span_function_body(node.body)
			)
		end
	elseif node.kind == "ifelse" then
		node = node :: cst.Expr_IfElse -- Luau Bug

		return merge(
			node.if_tok.span,
			span_expr(node.else_body)
		)
	elseif node.kind == "var" then
		return span_var(node.var)
	elseif node.kind == "assertion" then
		node = node :: cst.Expr_Assertion -- Luau Bug

		return merge(
			span_expr(node.expr),
			span_type(node.type)
		)
	elseif node.kind == "unary" then
		return merge(
			node.operator.span,
			span_expr(node.expr)
		)
	elseif node.kind == "binary" then
		node = node :: cst.Expr_Binary -- Luau Bug

		return merge(
			span_expr(node.left),
			span_expr(node.right)
		)
	else
		return unreachable(node.kind)
	end
end

function span_function_arg(node: cst.FunctionArg): cst.Span
	if node.kind == "pack" then
		local parens = node.parens
		return merge(
			parens.open.span,
			parens.close.span
		)
	elseif node.kind == "table" then
		return span_table(node.table)
	elseif node.kind == "string" then
		return node.tok.span
	else
		return unreachable(node.kind)
	end
end

function span_var_root(node: cst.VarRoot): cst.Span
	if node.kind == "name" then
		return node.tok.span
	elseif node.kind == "paren" then
		return merge(
			node.parens.open.span,
			node.parens.close.span
		)
	else
		return unreachable(node.kind)
	end
end

function span_var_suffix(node: cst.VarSuffix): cst.Span
	if node.kind == "nameindex" then
		return merge(
			node.dot.span,
			node.name.span
		)
	elseif node.kind == "exprindex" then
		local bracks = node.bracks
		return merge(
			bracks.open.span,
			bracks.close.span
		)
	elseif node.kind == "call" then
		local method = node.method
		local arg = node.arg

		if method then
			return merge(
				method.colon.span,
				span_function_arg(arg)
			)
		else
			return span_function_arg(arg)
		end
	else
		return unreachable(node.kind)
	end
end

function span_var(node: cst.Var): cst.Span
	local root = node.root
	local last = node.suffixes[(#node.suffixes :: any) :: number]

	if last then
		return merge(
			span_var_root(root),
			span_var_suffix(last)
		)
	else
		return span_var_root(root)
	end
end

function span_table_field(node: cst.TableField): cst.Span
	if node.kind == "namekey" then
		return merge(
			node.name.span,
			span_expr(node.value)
		)
	elseif node.kind == "exprkey" then
		local bracks = node.bracks
		return merge(
			bracks.open.span,
			span_expr(node.value)
		)
	elseif node.kind == "nokey" then
		return span_expr(node.value)
	else
		return unreachable(node.kind)
	end
end

function span_table(node: cst.Table): cst.Span
	local braces = node.braces
	return merge(
		braces.open.span,
		braces.close.span
	)
end

function span_function_body(node: cst.FunctionBody): cst.Span
	local generics = node.generics

	if generics then
		return merge(
			generics.angles.open.span,
			node.end_tok.span
		)
	else
		return merge(
			node.parens.open.span,
			node.end_tok.span
		)
	end
end

function span_if_stat_branch(node: cst.IfStatBranch): cst.Span
	local block = node.block

	if (#block.stats :: any) == 0 then
		return merge(
			span_expr(node.condition),
			node.then_tok.span
		)
	else
		return merge(
			span_expr(node.condition),
			span_block(block)
		)
	end
end

function span_stat(node: cst.Stat): cst.Span
	if node.kind == "assign" then
		local first = node.left[1]
		local last = node.right[(#node.right :: any) :: number]

		return merge(
			span_var(first.node),
			span_expr(last.node)
		)
	elseif node.kind == "compoundassign" then
		node = node :: cst.Stat_CompoundAssign -- Luau Bug

		return merge(
			span_var(node.left),
			span_expr(node.right)
		)
	elseif node.kind == "call" then
		return span_var(node.call)
	elseif node.kind == "do" then
		return merge(
			node.do_tok.span,
			node.end_tok.span
		)
	elseif node.kind == "while" then
		return merge(
			node.while_tok.span,
			node.end_tok.span
		)
	elseif node.kind == "repeat" then
		return merge(
			node.repeat_tok.span,
			span_expr(node.condition)
		)
	elseif node.kind == "if" then
		return merge(
			node.if_tok.span,
			node.end_tok.span
		)
	elseif node.kind == "numericfor" or node.kind == "forin" then
		return merge(
			node.for_tok.span,
			node.end_tok.span
		)
	elseif node.kind == "function" then
		local attribute = node.attributes[1]

		if attribute then
			return merge(
				attribute.span,
				span_function_body(node.body)
			)
		else
			return merge(
				node.function_tok.span,
				span_function_body(node.body)
			)
		end
	elseif node.kind == "localfunction" then
		node = node :: cst.Stat_LocalFunction -- Luau Bug

		local attribute = node.attributes[1]

		if attribute then
			return merge(
				attribute.span,
				span_function_body(node.body)
			)
		else
			return merge(
				node.local_tok.span,
				span_function_body(node.body)
			)
		end
	elseif node.kind == "localvariable" then
		local values = node.values

		if values then
			local exprs = values.exprs
			local last = exprs[(#exprs :: any) :: number]

			return merge(
				node.local_tok.span,
				span_expr(last.node)
			)
		else
			local bindings = node.bindings
			local last = bindings[(#bindings :: any) :: number]

			return merge(
				node.local_tok.span,
				span_binding(last.node)
			)
		end
	elseif node.kind == "type" then
		node = node :: cst.Stat_Type -- Luau Bug

		local export_tok = node.export_tok

		if export_tok then
			return merge(
				export_tok.span,
				span_type(node.type)
			)
		else
			return merge(
				node.type_tok.span,
				span_type(node.type)
			)
		end
	else
		return unreachable(node.kind)
	end
end

function span_last_stat(node: cst.LastStat): cst.Span
	if node.kind == "return" then
		local exprs = node.exprs
		local last = exprs[(#exprs :: any) :: number]

		return merge(
			node.return_tok.span,
			span_expr(last.node)
		)
	elseif node.kind == "break" or node.kind == "continue" then
		return node.tok.span
	else
		return unreachable(node.kind)
	end
end

function span_block(node: cst.Block): cst.Span
	local stats = node.stats
	local first = stats[1]
	local last_stat = node.last_stat

	if first == nil and last_stat == nil then
		error("attempt to get the span of an empty block")
	end

	if first == nil and last_stat ~= nil then
		local semicolon = last_stat.semicolon

		if semicolon then
			return merge(
				span_last_stat(last_stat.stat),
				semicolon.span
			)
		else
			return span_last_stat(last_stat.stat)
		end
	elseif last_stat ~= nil then
		local semicolon = last_stat.semicolon

		if semicolon then
			return merge(
				span_stat(first.node),
				semicolon.span
			)
		else
			return merge(
				span_stat(first.node),
				span_last_stat(last_stat.stat)
			)
		end
	else
		local last = stats[(#stats :: any) :: number]
		local sep = last.sep

		if sep then
			return merge(
				span_stat(first.node),
				sep.span
			)
		else
			return merge(
				span_stat(first.node),
				span_stat(last.node)
			)
		end
	end
end

return {
	generic_declaration = span_generic_declaration,
	generic_declaration_with_defaults = span_generic_declaration_with_defaults,
	type_arg = span_type_arg,
	table_type_field = span_table_type_field,
	tail_type = span_tail_type,
	type = span_type,
	type_pack = span_type_pack,
	return_type = span_return_type,
	binding = span_binding,
	istring = span_istring,
	if_else_expr_branch = span_if_else_expr_branch,
	expr = span_expr,
	function_arg = span_function_arg,
	var_root = span_var_root,
	var_suffix = span_var_suffix,
	var = span_var,
	table_field = span_table_field,
	table = span_table,
	function_body = span_function_body,
	if_stat_branch = span_if_stat_branch,
	stat = span_stat,
	last_stat = span_last_stat,
	block = span_block,
}