local Set = require(script.Parent.Parent:WaitForChild('Set'))
local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
local instanceOf = require(script.Parent.Parent.Parent:WaitForChild('instance-of'))
type Array<T> = types.Array<T>
type Set<T> = types.Set<T>
type Table = { [any]: any }

return function(value: Set<any> | Table | string): Array<string>
	if value == nil then
		error("cannot extract keys from a nil value")
	end

	local valueType = typeof(value)

	local keys
	if valueType == "table" then
		keys = {}
		if instanceOf(value, Set) then
			return keys
		end

		for key in pairs(value :: Table) do
			table.insert(keys, key)
		end
	elseif valueType == "string" then
		local length = (value :: string):len()
		keys = table.create(length)
		for i = 1, length do
			keys[i] = tostring(i)
		end
	end

	return keys
end


local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Array<T> = types.Array<T>
type Object = types.Object

-- FIXME: This should be updated to be closer to the actual
-- `Object.preventExtensions` functionality in JS. This requires additional
-- support from the VM
local function preventExtensions<T>(t: T & (Object | Array<any>)): T
	local name = tostring(t)

	return (
		setmetatable(t :: any, {
			__newindex = function(self, key, value)
				local message = ("%q (%s) is not a valid member of %s"):format(tostring(key), typeof(key), name)

				error(message, 2)
			end,
			__metatable = false,
		}) :: any
	) :: T
end

return preventExtensions


local types = require(script.Parent.Parent.Parent:WaitForChild('es7-types'))
type Array<T> = types.Array<T>

-- TODO Luau: needs overloads to model this more correctly
return function<T>(value: { [string]: T } | Array<T> | string): Array<T> | Array<string>
	if value == nil then
		error("cannot extract values from a nil value")
	end
	local valueType = typeof(value)

	local array: Array<T> | Array<string>
	if valueType == "table" then
		array = {} :: Array<T>
		for _, keyValue in pairs(value :: { [string]: T } | Array<T>) do
			table.insert(array, keyValue)
		end
	elseif valueType == "string" then
		-- optimization to avoid rehashing/growth
		local valueStringLength = (value :: string):len()
		array = table.create(valueStringLength)
		for i = 1, valueStringLength do
			(array :: Array<string>)[i] = (value :: string):sub(i, i)
		end
	end

	return array
end


--!nonstrict
local __DEV__ = _G.__DEV__

local inspect = require(script.Parent:WaitForChild('inspect'))
local isArray = require(script.Parent:WaitForChild('Array'):WaitForChild('isArray'))
local arrayForEach = require(script.Parent:WaitForChild('Array'):WaitForChild('forEach'))
local arrayFromString = require(script.Parent:WaitForChild('Array'):WaitForChild('from'):WaitForChild('fromString'))

local types = require(script.Parent.Parent:WaitForChild('es7-types'))

type Array<T> = types.Array<T>
type Object = types.Object
type setCallbackFn<T> = types.setCallbackFn<T>
type setCallbackFnWithThisArg<T> = types.setCallbackFnWithThisArg<T>
export type Set<T> = types.Set<T>

type Set_Statics = {
	new: <T>(iterable: Array<T> | Set<T> | string | nil) -> Set<T>
}

local Set: Set<any> & Set_Statics = (
	{
		__iter = function(self)
			return next, self._array
		end,
		__tostring = function(self)
			local result = "Set "
			if #self._array > 0 then
				result ..= "(" .. tostring(#self._array) .. ") "
			end
			result ..= inspect(self._array)
			return result
		end,
	} :: any
) :: Set<any> & Set_Statics;
(Set :: any).__index = Set

function Set.new<T>(iterable: Array<T> | Set<T> | string | nil): Set<T>
	local array
	local map = {}
	if iterable ~= nil then
		local arrayIterable

		if typeof(iterable) == "table" then
			if isArray(iterable) then
				arrayIterable = table.clone(iterable)
			else
				local mt = getmetatable(iterable :: any)
				if mt and rawget(mt, "__iter") then
					arrayIterable = iterable :: Set<T>
				elseif __DEV__ then
					error("cannot create array from an object-like table")
				end
			end
		elseif typeof(iterable) == "string" then
			-- TODO Luau: need overloads for `from` to avoid needing the manual cast
			arrayIterable = arrayFromString(iterable :: string) :: Array<string>
		else
			error(("cannot create array from value of type `%s`"):format(typeof(iterable)))
		end

		if arrayIterable then
			array = table.create(#arrayIterable)
			for _, element in arrayIterable do
				if not map[element] then
					map[element] = true
					table.insert(array, element)
				end
			end
		else
			array = {}
		end
	else
		array = {}
	end

	return (setmetatable({
		size = #array,
		_map = map,
		_array = array,
	}, Set) :: any) :: Set<T>
end

function Set:add(value)
	if not self._map[value] then
		-- Luau FIXME: analyze should know self is Set<T> which includes size as a number
		self.size = self.size :: number + 1
		self._map[value] = true
		table.insert(self._array, value)
	end
	return self
end

function Set:clear()
	self.size = 0
	table.clear(self._map)
	table.clear(self._array)
end

function Set:delete(value): boolean
	if not self._map[value] then
		return false
	end
	-- Luau FIXME: analyze should know self is Map<K, V> which includes size as a number
	self.size = self.size :: number - 1
	self._map[value] = nil
	local index = table.find(self._array, value)
	if index then
		table.remove(self._array, index)
	end
	return true
end

-- Implements Javascript's `Map.prototype.forEach` as defined below
-- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/forEach
function Set:forEach(callback: setCallbackFn<any> | setCallbackFnWithThisArg<any>, thisArg: Object?): ()
	if typeof(callback) ~= "function" then
		error("callback is not a function")
	end

	-- note: we can't turn this into a simple for-in loop, because the callbacks can modify the table and React, GQL, and Jest rely on JS behavior in that scenario
	arrayForEach(self._array, function(value)
		if thisArg ~= nil then
			(callback :: setCallbackFnWithThisArg<any>)(thisArg, value, value, self)
		else
			(callback :: setCallbackFn<any>)(value, value, self)
		end
	end)
end

function Set:has(value): boolean
	return self._map[value] ~= nil
end

function Set:ipairs()
	if __DEV__ then
		warn(
			debug.traceback(
				"`for _,_ in mySet:ipairs() do` is deprecated and will be removed in a future release, please use `for _,_ in mySet do` instead\n",
				2
			)
		)
	end
	return ipairs(self._array)
end

return Set


local ES7Types = require(script.Parent.Parent:WaitForChild('es7-types'))

type WeakMap<K, V> = ES7Types.WeakMap<K, V>

type WeakMapPrivate<K, V> = {
	_weakMap: { [K]: V },
	-- method definitions
	get: (self: WeakMapPrivate<K, V>, K) -> V,
	set: (self: WeakMapPrivate<K, V>, K, V) -> WeakMapPrivate<K, V>,
	has: (self: WeakMapPrivate<K, V>, K) -> boolean
}
type WeakMap_Statics = {
	new: <K, V>() -> WeakMap<K, V>
}

local WeakMap: WeakMapPrivate<any, any> & WeakMap_Statics = {} :: any;
(WeakMap :: any).__index = WeakMap

function WeakMap.new<K, V>(): WeakMap<K, V>
	local weakMap = setmetatable({}, { __mode = "k" })
	return setmetatable({ _weakMap = weakMap }, WeakMap) :: any
end

function WeakMap:get(key)
	return self._weakMap[key]
end

function WeakMap:set(key, value)
	self._weakMap[key] = value
	return self
end

function WeakMap:has(key): boolean
	return self._weakMap[key] ~= nil
end

return WeakMap :: WeakMap<any, any> & WeakMap_Statics


local Array = require(script:WaitForChild('Array'))
local Map = require(script:WaitForChild('Map'))
local Object = require(script:WaitForChild('Object'))
local Set = require(script:WaitForChild('Set'))
local WeakMap = require(script:WaitForChild('WeakMap'))
local inspect = require(script:WaitForChild('inspect'))

local types = require(script.Parent:WaitForChild('es7-types'))

export type Array<T> = types.Array<T>
export type Map<T, V> = types.Map<T, V>
export type Object = types.Object
export type Set<T> = types.Set<T>
export type WeakMap<T, V> = types.WeakMap<T, V>

return {
	Array = Array,
	Object = Object,
	Map = Map.Map,
	coerceToMap = Map.coerceToMap,
	coerceToTable = Map.coerceToTable,
	Set = Set,
	WeakMap = WeakMap,
	inspect = inspect,
}


local REQUIRED_MODULE = require(script.Parent.Parent["jsdotlua_es7-types@1.2.7"]["es7-types"])
export type Object = REQUIRED_MODULE.Object
export type Array<T> = REQUIRED_MODULE.Array<T>
export type Function = REQUIRED_MODULE.Function
export type Table<T, V> = REQUIRED_MODULE.Table<T, V>
export type Tuple<T, V> = REQUIRED_MODULE.Tuple<T, V>
export type mapCallbackFn<K, V> = REQUIRED_MODULE.mapCallbackFn<K, V>
export type mapCallbackFnWithThisArg<K, V> = REQUIRED_MODULE.mapCallbackFnWithThisArg<K, V>
export type Map<K, V> = REQUIRED_MODULE.Map<K, V>
export type WeakMap<K, V> = REQUIRED_MODULE.WeakMap<K, V>
export type setCallbackFn<T> = REQUIRED_MODULE.setCallbackFn<T>
export type setCallbackFnWithThisArg<T> = REQUIRED_MODULE.setCallbackFnWithThisArg<T>
export type Set<T> = REQUIRED_MODULE.Set<T>
return REQUIRED_MODULE


local REQUIRED_MODULE = require(script.Parent.Parent["jsdotlua_collections@1.2.7"]["collections"])
export type Array<T> = REQUIRED_MODULE.Array<T>
export type Map<T, V> = REQUIRED_MODULE.Map<T, V>
export type Object = REQUIRED_MODULE.Object
export type Set<T> = REQUIRED_MODULE.Set<T>
export type WeakMap<T, V> = REQUIRED_MODULE.WeakMap<T, V>
return REQUIRED_MODULE


local makeConsoleImpl = require(script:WaitForChild('makeConsoleImpl'))

return makeConsoleImpl()


local inspect = require(script.Parent.Parent:WaitForChild('collections')).inspect

local INDENT = "  "

return function()
	local console = {}
	local indentDepth = 0

	local function indent()
		return string.rep(INDENT, indentDepth)
	end

	function console.log(content, ...)
		local message
		if typeof(content) == "string" then
			message = string.format(content, ...)
		else
			message = inspect(content)
		end
		print(indent() .. message)
	end

	function console.debug(content, ...)
		local message
		if typeof(content) == "string" then
			message = string.format(content, ...)
		else
			message = inspect(content)
		end
		print(indent() .. message)
	end

	function console.info(content, ...)
		local message
		if typeof(content) == "string" then
			message = string.format(content, ...)
		else
			message = inspect(content)
		end
		print(indent() .. message)
	end

	function console.warn(content, ...)
		local message
		if typeof(content) == "string" then
			message = string.format(content, ...)
		else
			message = inspect(content)
		end
		warn(indent() .. message)
	end

	function console.error(content, ...)
		-- JS' `console.error` doesn't interrupt execution like Lua's `error`,
		-- which is more similar to throwing an exception in JS.
		local message
		if typeof(content) == "string" then
			message = string.format(content, ...)
		else
			message = inspect(content)
		end
		warn(indent() .. message)
	end

	function console.group(content, ...)
		local message
		if typeof(content) == "string" then
			message = string.format(content, ...)
		else
			message = inspect(content)
		end
		print(indent() .. message)
		indentDepth = indentDepth + 1
	end

	function console.groupCollapsed(content, ...)
		-- There's no smart console, so this is equivalent to `console.group`
		local message
		if typeof(content) == "string" then
			message = string.format(content, ...)
		else
			message = inspect(content)
		end
		print(indent() .. message)
		indentDepth = indentDepth + 1
	end

	function console.groupEnd()
		if indentDepth > 0 then
			indentDepth = indentDepth - 1
		end
	end

	return console
end


export type Object = { [string]: any }
export type Array<T> = { [number]: T }
export type Function = (...any) -> ...any
export type Table<T, V> = { [T]: V }
export type Tuple<T, V> = Array<T | V>
export type mapCallbackFn<K, V> = (element: V, key: K, map: Map<K, V>) -> ()
export type mapCallbackFnWithThisArg<K, V> = (thisArg: Object, value: V, key: K, map: Map<K, V>) -> ()

export type Map<K, V> = typeof(setmetatable(
	{} :: {
		size: number,
		-- method definitions
		set: (self: Map<K, V>, K, V) -> Map<K, V>,
		get: (self: Map<K, V>, K) -> V | nil,
		clear: (self: Map<K, V>) -> (),
		delete: (self: Map<K, V>, K) -> boolean,
		forEach: (
			self: Map<K, V>,
			callback: mapCallbackFn<K, V> | mapCallbackFnWithThisArg<K, V>,
			thisArg: Object?
		) -> (),
		[K]: V,
		has: (self: Map<K, V>, K) -> boolean,
		keys: (self: Map<K, V>) -> Array<K>,
		values: (self: Map<K, V>) -> Array<V>,
		entries: (self: Map<K, V>) -> Array<Tuple<K, V>>,
		ipairs: (self: Map<K, V>) -> any,
		_map: { [K]: V },
		_array: { [number]: K },
		__index: (self: Map<K, V>, key: K) -> V,
		__iter: (self: Map<K, V>) -> (<K, V>({ [K]: V }, K?) -> (K?, V), V),
		__newindex: (self: Map<K, V>, key: K, value: V) -> ()
},
	{} :: {
		__index: Map<K, V>,
		__iter: (self: Map<K, V>) -> (<K, V>({ [K]: V }, K?) -> (K, V), V),
		__newindex: (self: Map<K, V>, key: K, value: V) -> ()
}
))

export type WeakMap<K, V> = {
	-- method definitions
	get: (self: WeakMap<K, V>, K) -> V,
	set: (self: WeakMap<K, V>, K, V) -> WeakMap<K, V>,
	has: (self: WeakMap<K, V>, K) -> boolean
}

export type setCallbackFn<T> = (value: T, key: T, set: Set<T>) -> ()
export type setCallbackFnWithThisArg<T> = (thisArg: Object, value: T, key: T, set: Set<T>) -> ()

export type Set<T> = typeof(setmetatable(
	{} :: {
		size: number,
		-- method definitions
		add: (self: Set<T>, T) -> Set<T>,
		clear: (self: Set<T>) -> (),
		delete: (self: Set<T>, T) -> boolean,
		forEach: (self: Set<T>, callback: setCallbackFn<T> | setCallbackFnWithThisArg<T>, thisArg: Object?) -> (),
		has: (self: Set<T>, T) -> boolean,
		ipairs: (self: Set<T>) -> any
},
	{} :: {
		__index: Set<T>,
		__iter: (self: Set<T>) -> (<K, V>({ [K]: V }, K?) -> (K, V), T)
}
))
return {}


local instanceof = require(script:WaitForChild('instanceof'))

return instanceof


-- polyfill for https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof
local __DEV__ = _G.__DEV__

-- FIXME Luau: typing class as Object gives: Type '{ @metatable {| __call: <a>(a, ...any) -> Error, __tostring: <b, c>({+ message: b, name: c +}) -> string |}, Error }' could not be converted into 'table'
local function instanceof(tbl: any, class: any): boolean
	if __DEV__ then
		assert(typeof(class) == "table", "Received a non-table as the second argument for instanceof")
	end

	if typeof(tbl) ~= "table" then
		return false
	end

	local ok, hasNew = pcall(function()
		return class.new ~= nil and tbl.new == class.new
	end)
	if ok and hasNew then
		return true
	end

	local seen = { [tbl] = true }

	while tbl and typeof(tbl) == "table" do
		tbl = getmetatable(tbl)
		if typeof(tbl) == "table" then
			tbl = tbl.__index

			if tbl == class then
				return true
			end
		end

		-- if we still have a valid table then check against seen
		if typeof(tbl) == "table" then
			if seen[tbl] then
				return false
			end
			seen[tbl] = true
		end
	end

	return false
end

return instanceof


local AssertionErrorModule = require(script:WaitForChild('AssertionError.global'))

export type AssertionError = AssertionErrorModule.AssertionError

return AssertionErrorModule.AssertionError


local Error = require(script:WaitForChild('Error.global'))
export type Error = Error.Error
return Error


-- this maps onto community promise libraries which won't support Luau, so we inline
export type PromiseLike<T> = {
	andThen: (
		self: PromiseLike<T>,
		resolve: ((T) -> ...(nil | T | PromiseLike<T>))?,
		reject: ((any) -> ...(nil | T | PromiseLike<T>))?
	) -> PromiseLike<T>
}

type PromiseStatus = "Started" | "Resolved" | "Rejected" | "Cancelled"

export type Promise<T> = {
	andThen: (
		self: Promise<T>,
		resolve: ((T) -> ...(nil | T | PromiseLike<T>))?,
		reject: ((any) -> ...(nil | T | PromiseLike<T>))?
	) -> Promise<T>,

	catch: (Promise<T>, ((any) -> ...(nil | T | PromiseLike<nil>))) -> Promise<T>,

	onCancel: (Promise<T>, (() -> ())?) -> boolean,

	expect: (Promise<T>) -> ...T,

	-- FIXME Luau: need union type packs to parse  (...T) | () | PromiseLike<T> here
	await: (Promise<T>) -> (boolean, ...(T | any)),

	getStatus: (self: Promise<T>) -> PromiseStatus,
	-- FIXME Luau: need union type packs to parse  (...T) | () | PromiseLike<T> here
	awaitStatus: (self: Promise<T>) -> (PromiseStatus, ...(T | any))
}

return {}


-- reference documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
local HttpService = game:GetService("HttpService")

local String = require(script.Parent.Parent:WaitForChild('string'))
local charCodeAt = String.charCodeAt
local Error = require(script.Parent:WaitForChild('Error'))

local function encodeURIComponent(value: string): string
	local valueLength = utf8.len(value)
	if valueLength == 0 or valueLength == nil then
		return ""
	end
	-- we don't exhaustively test the whole string for invalid characters like ECMA-262 15.1.3 says
	local check = charCodeAt(value, 1)
	if valueLength == 1 then
		if check == 0xD800 then
			error(Error.new("URI malformed"))
		end
		if check == 0xDFFF then
			error(Error.new("URI malformed"))
		end
	end
	if check >= 0xDC00 and check < 0xDFFF then
		error(Error.new("URI malformed"))
	end
	local encoded = HttpService:UrlEncode(value)
	-- reverting encoded chars which are not encoded by JS
	local result = encoded
		:gsub("%%2D", "-")
		:gsub("%%5F", "_")
		:gsub("%%2E", ".")
		:gsub("%%21", "!")
		:gsub("%%7E", "~")
		:gsub("%%2A", "*")
		:gsub("%%27", "'")
		:gsub("%%28", "(")
		:gsub("%%29", ")")
	return result
end

return encodeURIComponent


--!nonstrict
--[[
	deviation: Our constructors currently have no notion of 'super' so any
	such behavior in upstream JS must be implemented manually by setting fields

	A constructor passed to this class would typically look along the lines of:
	function(self, arg, otherArg)
		self.arg = arg
		self.otherArg = otherArg
	end

	But even something like function() end can be passed in as the constructor
	if no fields need to be set. In upstream JS, the equivalent would be
	returning an empty object from the constructor.
]]
return function(parent, childName, constructor)
	local Child = {}
	Child.__index = Child
	Child.__tostring = function(self)
		return getmetatable(parent).__tostring(self)
	end

	local mt = {}

	Child.new = function(...)
		local self = {}
		constructor(self, ...)
		return setmetatable(self, Child)
	end

	if typeof(getmetatable(parent)) == "table" and getmetatable(parent).__call then
		mt.__call = function(_, ...)
			return Child.new(...)
		end
	end

	mt.__index = parent
	mt.__tostring = function(self)
		-- Printing class name
		if self == Child then
			return tostring(childName)
		end
		return getmetatable(parent).__tostring(self)
	end

	setmetatable(Child, mt)

	return Child
end


local Boolean = require(script.Parent:WaitForChild('boolean'))
local Collections = require(script.Parent:WaitForChild('collections'))
local Console = require(script.Parent:WaitForChild('console'))
local Math = require(script.Parent:WaitForChild('math'))
local Number = require(script.Parent:WaitForChild('number'))
local String = require(script.Parent:WaitForChild('string'))
local Symbol = require(script.Parent:WaitForChild('symbol-luau'))
local Timers = require(script.Parent:WaitForChild('timers'))
local types = require(script.Parent:WaitForChild('es7-types'))

local AssertionError = require(script:WaitForChild('AssertionError'))
local Error = require(script:WaitForChild('Error'))
local PromiseModule = require(script:WaitForChild('Promise'))
local extends = require(script:WaitForChild('extends'))
local instanceof = require(script.Parent:WaitForChild('instance-of'))

export type Array<T> = types.Array<T>
export type AssertionError = AssertionError.AssertionError
export type Error = Error.Error
export type Map<T, V> = types.Map<T, V>
export type Object = types.Object

export type PromiseLike<T> = PromiseModule.PromiseLike<T>
export type Promise<T> = PromiseModule.Promise<T>

export type Set<T> = types.Set<T>
export type Symbol = Symbol.Symbol
export type Timeout = Timers.Timeout
export type Interval = Timers.Interval
export type WeakMap<T, V> = Collections.WeakMap<T, V>

return {
	Array = Collections.Array,
	AssertionError = AssertionError,
	Boolean = Boolean,
	console = Console,
	Error = Error,
	extends = extends,
	instanceof = instanceof,
	Math = Math,
	Number = Number,
	Object = Collections.Object,
	Map = Collections.Map,
	coerceToMap = Collections.coerceToMap,
	coerceToTable = Collections.coerceToTable,
	Set = Collections.Set,
	WeakMap = Collections.WeakMap,
	String = String,
	Symbol = Symbol,
	setTimeout = Timers.setTimeout,
	clearTimeout = Timers.clearTimeout,
	setInterval = Timers.setInterval,
	clearInterval = Timers.clearInterval,
	util = {
		inspect = Collections.inspect,
	},
}


local REQUIRED_MODULE = require(script.Parent.Parent["jsdotlua_symbol-luau@1.0.1"]["symbol-luau"])
export type Symbol = REQUIRED_MODULE.Symbol
return REQUIRED_MODULE


local REQUIRED_MODULE = require(script.Parent.Parent["jsdotlua_timers@1.2.7"]["timers"])
export type Timeout = REQUIRED_MODULE.Timeout
export type Interval = REQUIRED_MODULE.Interval
return REQUIRED_MODULE


return {
	isFinite = require(script:WaitForChild('isFinite')),
	isInteger = require(script:WaitForChild('isInteger')),
	isNaN = require(script:WaitForChild('isNaN')),
	isSafeInteger = require(script:WaitForChild('isSafeInteger')),
	MAX_SAFE_INTEGER = require(script:WaitForChild('MAX_SAFE_INTEGER')),
	MIN_SAFE_INTEGER = require(script:WaitForChild('MIN_SAFE_INTEGER')),
	NaN = 0/0,
	toExponential = require(script:WaitForChild('toExponential')),
}


return function(value)
	return typeof(value) == "number" and value == value and value ~= math.huge and value ~= -math.huge
end


-- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger
return function(value)
	return type(value) == "number" and value ~= math.huge and value == math.floor(value)
end


-- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN
return function(value)
	return type(value) == "number" and value ~= value
end


-- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger
local isInteger = require(script.Parent:WaitForChild('isInteger'))
local MAX_SAFE_INTEGER = require(script.Parent:WaitForChild('MAX_SAFE_INTEGER'))

return function(value)
	return isInteger(value) and math.abs(value) <= MAX_SAFE_INTEGER
end


-- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toExponential
return function(value: string | number, fractionDigits: number?): string | nil
	local num = value
	if typeof(value) == "string" then
		-- ROBLOX FIXME: add parseInt to encapsulate this logic and use it here
		local NaN = 0/0
num = tonumber(value) or NaN -- works because 0 is truthy in Lua
	end
	-- returns nil for invalid input instead of nan to be more lua-native
	if typeof(num) ~= "number" then
		return "nan"
	end

	if fractionDigits ~= nil then
		if typeof(fractionDigits) ~= "number" then
			error("TypeError: fractionDigits must be a number between 0 and 100")
		end
		if fractionDigits < 0 or fractionDigits > 100 then
			error("RangeError: fractionDigits must be between 0 and 100")
		end
	end

	local formatString
	if fractionDigits == nil then
		formatString = "%e"
	else
		formatString = "%." .. tostring(fractionDigits) .. "e"
	end

	local retval = string.format(formatString, num):gsub("%+0", "+"):gsub("%-0", "-"):gsub("0*e", "e")

	return retval
end


local REQUIRED_MODULE = require(script.Parent.Parent["jsdotlua_luau-polyfill@1.2.7"]["luau-polyfill"])
export type Array<T> = REQUIRED_MODULE.Array<T>
export type AssertionError = REQUIRED_MODULE.AssertionError
export type Error = REQUIRED_MODULE.Error
export type Map<T, V> = REQUIRED_MODULE.Map<T, V>
export type Object = REQUIRED_MODULE.Object
export type PromiseLike<T> = REQUIRED_MODULE.PromiseLike<T>
export type Promise<T> = REQUIRED_MODULE.Promise<T>
export type Set<T> = REQUIRED_MODULE.Set<T>
export type Symbol = REQUIRED_MODULE.Symbol
export type Timeout = REQUIRED_MODULE.Timeout
export type Interval = REQUIRED_MODULE.Interval
export type WeakMap<T, V> = REQUIRED_MODULE.WeakMap<T, V>
return REQUIRED_MODULE


--[[
	* 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.
]]

-- Targeted performance insights for Roblox Microprofiler

local getComponentName = require(script.Parent.Parent:WaitForChild('shared')).getComponentName
local ReactWorkTags = require(script.Parent:WaitForChild('ReactWorkTags'))

local ReactInternalTypes = require(script.Parent:WaitForChild('ReactInternalTypes'))
type Fiber = ReactInternalTypes.Fiber
type FiberRoot = ReactInternalTypes.FiberRoot

-- ReactMicroprofilerLevel Levels --
local LEVEL_ROOTS = 1 -- Level 1: Roots + Commit time
local LEVEL_FIBERS = 10 -- Level 10: Individual Fiber "units of work"

local ReactMicroprofilerLevel = _G.__REACT_MICROPROFILER_LEVEL or 0

export type Marker = {
	id: string,
	startTime: number,
	endTime: number
}

export type SamplerCallback = (Marker) -> ()

local enableRootSampling = false
local timerSamplingCallback: SamplerCallback | nil

function startTimerSampling(timerSamplingCallbackFn: SamplerCallback)
	if enableRootSampling then
		warn("RobloxReactProfiling Timer Sampling already running.")
	end
	enableRootSampling = true
	timerSamplingCallback = timerSamplingCallbackFn
end

function endTimerSampling()
	enableRootSampling = false
	timerSamplingCallback = nil
end

function getFirstStringKey(t: { any: any })
	for key, _ in t do
		if type(key) == "string" then
			return key
		end
	end
	return nil
end

function startTimer(marker: Marker)
	if enableRootSampling then
		marker.startTime = os.clock()
	end
end
function endTimer(marker: Marker)
	if enableRootSampling then
		marker.endTime = os.clock()
		if timerSamplingCallback then
			timerSamplingCallback(marker)
		end
	end
end

function profileRootBeforeUnitOfWork(root: FiberRoot): Marker?
	local rootFiber = root.current
	local profileId

if rootFiber then
		if rootFiber.memoizedProps then
			-- expecting props table with single item
			profileId = getFirstStringKey(rootFiber.memoizedProps)
		end

		if
			profileId == nil
			and rootFiber.stateNode
			and rootFiber.stateNode.containerInfo
		then
			profileId = rootFiber.stateNode.containerInfo.Name
		end
	end

	-- note: investigate HostRoot vs HostPortal for this condition
	if profileId == "Folder" and rootFiber.child then
		local fiber = rootFiber.child
		local folderProfileId
if fiber.memoizedProps then
			-- expecting props table with single item
			folderProfileId = getFirstStringKey(fiber.memoizedProps)
		end

		if
			folderProfileId == nil
			and fiber.stateNode
			and fiber.stateNode.containerInfo
		then
			folderProfileId = fiber.stateNode.containerInfo.Name
		end
		if folderProfileId ~= nil then
			profileId = folderProfileId
		end
	end

	if profileId ~= nil then
		local marker = {
			id = profileId,
			startTime = 0,
			endTime = 0,
		}
		startTimer(marker)
		debug.profilebegin(profileId)
		return marker
	end

	return nil
end

function profileRootAfterYielding(marker: Marker?)
	if marker then
		endTimer(marker)
		debug.profileend()
	end
end

function profileUnitOfWorkBefore(unitOfWork: Fiber)
	local profileId = getComponentName(unitOfWork.type)

	if unitOfWork.key then
		profileId = tostring(unitOfWork.key) .. "=" .. (profileId or "?")
	end

	local rootName
if unitOfWork.stateNode then
		if
			unitOfWork.tag == ReactWorkTags.HostComponent
			or unitOfWork.tag == ReactWorkTags.HostText
		then
			local layerCollector =
				unitOfWork.stateNode:FindFirstAncestorWhichIsA("LayerCollector")
			if layerCollector then
				rootName = "[" .. layerCollector:GetFullName() .. "] "
			end
		end
	end

	if rootName then
		profileId = rootName .. " : " .. (profileId or "?")
	end

	if profileId ~= nil then
		debug.profilebegin(profileId)
		return true
	end

	return false
end

function profileUnitOfWorkAfter(profileRunning: boolean)
	if profileRunning then
		debug.profileend()
	end
end

function profileCommitBefore()
	debug.profilebegin("Commit")
end
function profileCommitAfter()
	debug.profileend()
end

function noop(...: unknown) end

return {
	startTimerSampling = startTimerSampling,
	endTimerSampling = endTimerSampling,
	profileRootBeforeUnitOfWork = if ReactMicroprofilerLevel >= LEVEL_ROOTS
		then profileRootBeforeUnitOfWork
		else noop,
	profileRootAfterYielding = if ReactMicroprofilerLevel >= LEVEL_ROOTS
		then profileRootAfterYielding
		else noop,
	profileUnitOfWorkBefore = if ReactMicroprofilerLevel >= LEVEL_FIBERS
		then profileUnitOfWorkBefore
		else noop,
	profileUnitOfWorkAfter = if ReactMicroprofilerLevel >= LEVEL_FIBERS
		then profileUnitOfWorkAfter
		else noop,
	profileCommitBefore = if ReactMicroprofilerLevel >= LEVEL_ROOTS
		then profileCommitBefore
		else noop,
	profileCommitAfter = if ReactMicroprofilerLevel >= LEVEL_ROOTS
		then profileCommitAfter
		else noop,
}


local REQUIRED_MODULE = require(script.Parent.Parent["jsdotlua_react@17.2.1"]["react"])
export type Object = REQUIRED_MODULE.Object
export type Binding<T> = REQUIRED_MODULE.Binding<T>
export type BindingUpdater<T> = REQUIRED_MODULE.BindingUpdater<T>
export type LazyComponent<T, P> = REQUIRED_MODULE.LazyComponent<T, P>
export type StatelessFunctionalComponent
 =
REQUIRED_MODULE.StatelessFunctionalComponent

export type ComponentType
 = REQUIRED_MODULE.ComponentType

export type AbstractComponent<Config, Instance> = REQUIRED_MODULE.AbstractComponent<Config, Instance>
export type ElementType = REQUIRED_MODULE.ElementType
export type Element<C> = REQUIRED_MODULE.Element<C>
export type Key = REQUIRED_MODULE.Key
export type Ref<ElementType> = REQUIRED_MODULE.Ref<ElementType>
export type Node = REQUIRED_MODULE.Node
export type Context<T> = REQUIRED_MODULE.Context<T>
export type ElementProps<C> = REQUIRED_MODULE.ElementProps<C>
export type ElementConfig<T> = REQUIRED_MODULE.ElementConfig<T>
export type ElementRef<C> = REQUIRED_MODULE.ElementRef<C>
export type ComponentClass
 = REQUIRED_MODULE.ComponentClass

export type PureComponent<Props, State > = REQUIRED_MODULE.PureComponent<Props, State >
export type ReactElement<Props , ElementType > = REQUIRED_MODULE.ReactElement<Props , ElementType >
export type ReactChild = REQUIRED_MODULE.ReactChild
export type FC
 = REQUIRED_MODULE.FC

export type ReactNode = REQUIRED_MODULE.ReactNode
export type React_AbstractComponent<Props, Instance> = REQUIRED_MODULE.React_AbstractComponent<Props, Instance>
export type React_FowardRefComponent<Props, Instance> =
REQUIRED_MODULE.React_FowardRefComponent<Props, Instance>
export type React_MemoComponent<Config, T> = REQUIRED_MODULE.React_MemoComponent<Config, T>
export type React_Component<Props, State> = REQUIRED_MODULE.React_Component<Props, State>
export type React_ComponentType
 = REQUIRED_MODULE.React_ComponentType

export type React_Context<T> = REQUIRED_MODULE.React_Context<T>
export type React_Element<ElementType> = REQUIRED_MODULE.React_Element<ElementType>
export type React_ElementType = REQUIRED_MODULE.React_ElementType
export type React_Node = REQUIRED_MODULE.React_Node
return REQUIRED_MODULE


local REQUIRED_MODULE = require(script.Parent.Parent["jsdotlua_scheduler@17.2.1"]["scheduler"])
export type Interaction = REQUIRED_MODULE.Interaction
return REQUIRED_MODULE


local REQUIRED_MODULE = require(script.Parent.Parent["jsdotlua_shared@17.2.1"]["shared"])
export type ReactEmpty = REQUIRED_MODULE.ReactEmpty
export type ReactFragment = REQUIRED_MODULE.ReactFragment
export type ReactNodeList = REQUIRED_MODULE.ReactNodeList
export type ReactProviderType<T> = REQUIRED_MODULE.ReactProviderType<T>
export type ReactConsumer<T> = REQUIRED_MODULE.ReactConsumer<T>
export type ReactProvider<T> = REQUIRED_MODULE.ReactProvider<T>
export type ReactContext<T> = REQUIRED_MODULE.ReactContext<T>
export type ReactPortal = REQUIRED_MODULE.ReactPortal
export type RefObject = REQUIRED_MODULE.RefObject
export type EventPriority = REQUIRED_MODULE.EventPriority
export type ReactFundamentalComponentInstance<C, H> =
REQUIRED_MODULE.ReactFundamentalComponentInstance<C, H>
export type ReactFundamentalImpl<C, H> = REQUIRED_MODULE.ReactFundamentalImpl<C, H>
export type ReactFundamentalComponent<C, H> = REQUIRED_MODULE.ReactFundamentalComponent<C, H>
export type ReactScope = REQUIRED_MODULE.ReactScope
export type ReactScopeQuery = REQUIRED_MODULE.ReactScopeQuery
export type ReactScopeInstance = REQUIRED_MODULE.ReactScopeInstance
export type ReactBinding<T> = REQUIRED_MODULE.ReactBinding<T>
export type ReactBindingUpdater<T> = REQUIRED_MODULE.ReactBindingUpdater<T>
export type MutableSourceVersion = REQUIRED_MODULE.MutableSourceVersion
export type MutableSourceGetSnapshotFn<Source, Snapshot> =
REQUIRED_MODULE.MutableSourceGetSnapshotFn<Source, Snapshot>
export type MutableSourceSubscribeFn<Source, Snapshot> = REQUIRED_MODULE.MutableSourceSubscribeFn<Source, Snapshot>
export type MutableSourceGetVersionFn = REQUIRED_MODULE.MutableSourceGetVersionFn
export type MutableSource<Source> = REQUIRED_MODULE.MutableSource<Source>
export type Wakeable = REQUIRED_MODULE.Wakeable
export type Thenable<R> = REQUIRED_MODULE.Thenable<R>
export type Source = REQUIRED_MODULE.Source
export type ReactElement<P , T > = REQUIRED_MODULE.ReactElement<P , T >
export type OpaqueIDType = REQUIRED_MODULE.OpaqueIDType
export type Dispatcher = REQUIRED_MODULE.Dispatcher
export type React_Ref<ElementType> = REQUIRED_MODULE.React_Ref<ElementType>
export type React_Context<T> = REQUIRED_MODULE.React_Context<T>
export type React_AbstractComponent<Config, Instance> = REQUIRED_MODULE.React_AbstractComponent<Config, Instance>
export type React_ForwardRefComponent<Config, Instance> =
REQUIRED_MODULE.React_ForwardRefComponent<Config, Instance>
export type React_MemoComponent<Config, T> = REQUIRED_MODULE.React_MemoComponent<Config, T>
export type React_ComponentType<Config> = REQUIRED_MODULE.React_ComponentType<Config>
export type React_PureComponent<Props, State > = REQUIRED_MODULE.React_PureComponent<Props, State >
export type React_Component<Props, State> = REQUIRED_MODULE.React_Component<Props, State>
export type React_ElementProps<ElementType> = REQUIRED_MODULE.React_ElementProps<ElementType>
export type React_StatelessFunctionalComponent<Props> =
REQUIRED_MODULE.React_StatelessFunctionalComponent<Props>
export type React_Node = REQUIRED_MODULE.React_Node
export type React_Element<ElementType> = REQUIRED_MODULE.React_Element<ElementType>
export type React_ElementType = REQUIRED_MODULE.React_ElementType
export type React_ElementConfig<C> = REQUIRED_MODULE.React_ElementConfig<C>
export type React_ElementRef<C> = REQUIRED_MODULE.React_ElementRef<C>
export type React_Portal = REQUIRED_MODULE.React_Portal
export type React_Key = REQUIRED_MODULE.React_Key
return REQUIRED_MODULE


local REQUIRED_MODULE = require(script.Parent.Parent["jsdotlua_react-reconciler@17.2.1"]["react-reconciler"])
export type Dispatcher = REQUIRED_MODULE.Dispatcher
export type Fiber = REQUIRED_MODULE.Fiber
export type FiberRoot = REQUIRED_MODULE.FiberRoot
export type UpdateQueue<T> = REQUIRED_MODULE.UpdateQueue<T>
export type RootTag = REQUIRED_MODULE.RootTag
return REQUIRED_MODULE


--!strict
-- ROBLOX deviation: Initializes the reconciler with this package's host
-- config and returns the resulting module

local initializeReconciler = require(script.Parent.Parent:WaitForChild('react-reconciler'))

local ReactRobloxHostConfig = require(script.Parent:WaitForChild('client'):WaitForChild('ReactRobloxHostConfig'))

return initializeReconciler(ReactRobloxHostConfig)


--[[
	* 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 __DEV__ = _G.__DEV__ :: boolean
local CollectionService = game:GetService("CollectionService")
local LuauPolyfill = require(script.Parent.Parent.Parent.Parent:WaitForChild('luau-polyfill'))
local Object = LuauPolyfill.Object
local inspect = LuauPolyfill.util.inspect

local console = require(script.Parent.Parent.Parent.Parent:WaitForChild('shared')).console

local React = require(script.Parent.Parent.Parent.Parent:WaitForChild('react'))
local ReactSymbols = require(script.Parent.Parent.Parent.Parent:WaitForChild('shared')).ReactSymbols
local SingleEventManager = require(script.Parent:WaitForChild('SingleEventManager'))
type EventManager = SingleEventManager.EventManager
local Type = require(script.Parent.Parent.Parent.Parent:WaitForChild('shared')).Type
local getDefaultInstanceProperty = require(script.Parent:WaitForChild('getDefaultInstanceProperty'))
local ReactRobloxHostTypes = require(script.Parent.Parent:WaitForChild('ReactRobloxHostTypes.roblox'))
type HostInstance = ReactRobloxHostTypes.HostInstance
local Tag = require(script.Parent.Parent.Parent.Parent:WaitForChild('react')).Tag

-- ROBLOX deviation: Essentially a placeholder for dom-specific logic, taking the place
-- of ReactDOMComponent. Most of the logic will differ pretty dramatically

type Array<T> = { [number]: T }
type Object = { [any]: any }

-- deviation: Can't assign attributes to Roblox instances, so we use maps to
-- store associated data for host instance features like binding and event
-- management
-- ROBLOX FIXME: Stronger typing for EventManager

local instanceToEventManager: { [HostInstance]: EventManager } = {}
local instanceToBindings: { [HostInstance]: { [string]: any } } = {}

local applyPropsError = [[
Error applying initial props to Roblox Instance '%s' (%s):
  %s
]]

local updatePropsError = [[
Error updating props on Roblox Instance '%s' (%s):
  %s
]]

local updateBindingError = [[
Error updating binding or ref assigned to key %s of '%s' (%s).

Updated value:
  %s

Error:
  %s

%s
]]

local function identity(...)
	return ...
end

local function setRobloxInstanceProperty(hostInstance, key, newValue): ()
	if newValue == nil then
		local success, _ = pcall(hostInstance.ResetPropertyToDefault, hostInstance, key)
		if success then
			return
		end

		local hostClass = hostInstance.ClassName
		local _, defaultValue = getDefaultInstanceProperty(hostClass, key)
		newValue = defaultValue
	end

	-- Assign the new value to the object
	hostInstance[key] = newValue
end

local function removeBinding(hostInstance, key)
	local bindings = instanceToBindings[hostInstance]
	if bindings ~= nil then
		local disconnect = bindings[key]
		disconnect()
		bindings[key] = nil
	end
end

local function attachBinding(hostInstance, key, newBinding): ()
	local function updateBoundProperty(newValue)
		local success, errorMessage =
			xpcall(setRobloxInstanceProperty, identity, hostInstance, key, newValue)

		if not success then
			local source = newBinding._source or "<enable DEV mode for stack>"
			local fullMessage = string.format(
				updateBindingError,
				key,
				hostInstance.Name,
				hostInstance.ClassName,
				tostring(newValue),
				errorMessage,
				source
			)
			console.error(fullMessage)
			-- FIXME: Until console.error can be instrumented to send telemetry, we
			-- need to keep the hard error here
			error(fullMessage, 0)
		end
	end

	if instanceToBindings[hostInstance] == nil then
		instanceToBindings[hostInstance] = {}
	end

	instanceToBindings[hostInstance][key] =
		React.__subscribeToBinding(newBinding, updateBoundProperty)

	updateBoundProperty(newBinding:getValue())
end

local function applyTags(hostInstance: Instance, oldTags: string?, newTags: string?)
	if __DEV__ then
		if newTags ~= nil and typeof(newTags) ~= "string" then
			console.error(
[[Type provided for ReactRoblox.Tag is invalid - tags should be specified as a single string, with individual tags delimited by spaces. Instead received:
%s]]
,
				inspect(newTags)
			)
			return
		end
	end

	local oldTagSet = {}
	for str in string.gmatch(oldTags or "", "%S+") do
		oldTagSet[str] = true
	end
	local newTagSet = {}
	for str in string.gmatch(newTags or "", "%S+") do
		newTagSet[str] = true
	end

	for tag, _ in oldTagSet do
		if not newTagSet[tag] then
			CollectionService:RemoveTag(hostInstance, tag)
		end
	end
	for tag, _ in newTagSet do
		if not oldTagSet[tag] then
			CollectionService:AddTag(hostInstance, tag)
		end
	end
end

local function removeAllTags(hostInstance: Instance)
	for _, tag in CollectionService:GetTags(hostInstance) do
		CollectionService:RemoveTag(hostInstance, tag)
	end
end

local function applyProp(hostInstance: Instance, key, newValue, oldValue): ()
	-- ROBLOX performance: gets checked in applyProps so we can assume the key is valid
	-- if key == "ref" or key == "children" then
	--   return
	-- end

	local internalKeyType = Type.of(key)

	if internalKeyType == Type.HostEvent or internalKeyType == Type.HostChangeEvent then
		local eventManager = instanceToEventManager[hostInstance]
		if eventManager == nil then
			eventManager = (SingleEventManager.new(hostInstance) :: any) :: EventManager
			instanceToEventManager[hostInstance] = eventManager
		end

		local eventName = key.name

		if internalKeyType == Type.HostChangeEvent then
			eventManager:connectPropertyChange(eventName, newValue)
		else
			eventManager:connectEvent(eventName, newValue)
		end

		return
	end

	-- Handle bindings
	local newIsBinding = typeof(newValue) == "table"
		and newValue["$$typeof"] == ReactSymbols.REACT_BINDING_TYPE
	local oldIsBinding = oldValue ~= nil
		and typeof(oldValue) == "table"
		and oldValue["$$typeof"] == ReactSymbols.REACT_BINDING_TYPE
	if oldIsBinding then
		removeBinding(hostInstance, key)
	end

	if newIsBinding then
		attachBinding(hostInstance, key, newValue)
	elseif key == Tag then
		applyTags(hostInstance, oldValue, newValue)
	else
		setRobloxInstanceProperty(hostInstance, key, newValue)
	end
end

local function applyProps(hostInstance: Instance, props: Object): ()
	for propKey, value in props do
		-- ROBLOX performance: avoid the function call by inlining check here
		if propKey == "ref" or propKey == "children" then
			continue
		end

		applyProp(hostInstance, propKey, value)
	end
end

local function setInitialProperties(
	domElement: HostInstance,
	_tag: string,
	rawProps: Object,
	_rootContainerElement: HostInstance
): ()
	-- deviation: Use Roact's prop application logic
	local success, errorMessage = xpcall(applyProps, identity, domElement, rawProps)
	-- ROBLOX deviation: Roblox renderer doesn't currently track where instances
	-- were created the way that legacy Roact did, but DEV mode should include
	-- component stack traces as a separate warning
	if not success then
		local fullMessage = string.format(
			applyPropsError,
			domElement.Name,
			domElement.ClassName,
			errorMessage
		)
		console.error(fullMessage)
		-- FIXME: Until console.error can be instrumented to send telemetry, we need
		-- to keep the hard error here
		error(fullMessage, 0)
	end

	if instanceToEventManager[domElement] ~= nil then
		instanceToEventManager[domElement]:resume()
	end
end

local function safelyApplyProperties(
	domElement: HostInstance,
	updatePayload: Array<any>,
	lastProps: Object
): ()
	local updatePayloadCount = #updatePayload
	for i = 1, updatePayloadCount, 2 do
		local propKey = updatePayload[i]
		local value = updatePayload[i + 1]
		if value == Object.None then
			value = nil
		end
		-- ROBLOX performance: avoid the function call by inlining check here
		if propKey ~= "ref" and propKey ~= "children" then
			applyProp(domElement, propKey, value, lastProps[propKey])
		end
	end
end

local function updateProperties(
	domElement: HostInstance,
	updatePayload: Array<any>,
	lastProps: Object
): ()
	-- deviation: Use Roact's prop application logic
	if instanceToEventManager[domElement] ~= nil then
		instanceToEventManager[domElement]:suspend()
	end

	local success, errorMessage =
		xpcall(safelyApplyProperties, identity, domElement, updatePayload, lastProps)

	if not success then
		-- ROBLOX deviation: Roblox renderer doesn't currently track where instances
		-- were created the way that legacy Roact did, but DEV mode should include
		-- component stack traces as a separate warning
		local fullMessage = string.format(
			updatePropsError,
			domElement.Name,
			domElement.ClassName,
			errorMessage
		)
		console.error(fullMessage)
		-- FIXME: Until console.error can be instrumented to send telemetry, we need
		-- to keep the hard error here
		error(fullMessage, 0)
	end

	if instanceToEventManager[domElement] ~= nil then
		instanceToEventManager[domElement]:resume()
	end
end

local function cleanupBindings(domElement: HostInstance)
	local instanceBindings = instanceToBindings[domElement]
	if instanceBindings ~= nil then
		for _, disconnectBinding in instanceBindings do
			disconnectBinding()
		end
		instanceToBindings[domElement] = nil
	end
end

-- ROBLOX deviation: Clear out references to components when they unmount so we
-- avoid leaking memory when they're removed
local function cleanupHostComponent(domElement: HostInstance)
	if instanceToEventManager[domElement] ~= nil then
		instanceToEventManager[domElement] = nil
	end

	cleanupBindings(domElement)

	-- ROBLOX https://jira.rbx.com/browse/LUAFDN-718: Tables are somehow ending up
	-- in this function that expects Instances. In that case, we won't be able to
	-- iterate through its descendants.
	if typeof(domElement :: any) ~= "Instance" then
		return
	end

	removeAllTags(domElement)
	for _, descElement in domElement:GetDescendants() do
		if instanceToEventManager[descElement] ~= nil then
			instanceToEventManager[descElement] = nil
		end

		cleanupBindings(descElement)
		removeAllTags(domElement)
	end
end

return {
	setInitialProperties = setInitialProperties,
	updateProperties = updateProperties,
	cleanupHostComponent = cleanupHostComponent,

	-- ROBLOX deviation: expose maps to test for Instance cleanups
	_instanceToEventManager = instanceToEventManager,
	_instanceToBindings = instanceToBindings,
}


--!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 LuauPolyfill = require(script.Parent.Parent:WaitForChild('luau-polyfill'))
local ReactSymbols = require(script.Parent.Parent:WaitForChild('shared')).ReactSymbols

local ReactTypes = require(script.Parent.Parent:WaitForChild('shared'))
type Binding<T> = ReactTypes.ReactBinding<T>
type BindingUpdater<T> = ReactTypes.ReactBindingUpdater<T>

local Symbol = LuauPolyfill.Symbol
local createSignal = require(script.Parent:WaitForChild('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 _G.__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 _G.__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 _G.__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 _G.__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 _G.__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) 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:WaitForChild('Tracing'))
local TracingSubscriptions = require(script.Parent:WaitForChild('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:WaitForChild('Scheduler'))
local HostConfig = require(script.Parent:WaitForChild('forks'):WaitForChild('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 LuauPolyfill = require(script.Parent.Parent:WaitForChild('luau-polyfill'))
type Error = LuauPolyfill.Error
type Object = LuauPolyfill.Object
type Array<T> = LuauPolyfill.Array<T>
local Error = LuauPolyfill.Error
local inspect = LuauPolyfill.util.inspect

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

--[[
	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)
		err.stack = debug.traceback(nil, 2)
		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:WaitForChild('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:WaitForChild('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:WaitForChild('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 LuauPolyfill = require(script.Parent.Parent:WaitForChild('luau-polyfill'))
type Object = LuauPolyfill.Object

local flowtypes = require(script.Parent:WaitForChild('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:WaitForChild('WithNoHydration')),
	WithNoPersistence = require(script:WaitForChild('WithNoPersistence')),
	WithNoTestSelectors = require(script:WaitForChild('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 LuauPolyfill = require(script.Parent.Parent:WaitForChild('luau-polyfill'))
type Array<T> = LuauPolyfill.Array<T>
type Object = LuauPolyfill.Object
type void = nil
type NonMaybeType<T> = T

local flowtypes = require(script.Parent:WaitForChild('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 = { current: any }

-- 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.
]]
--[[
	A 'Symbol' is an opaque marker type.

	Symbols have the type 'userdata', but when printed to the console, the name
	of the symbol is shown.
]]

local Symbol = {}

--[[
	Creates a Symbol with the given name.

	When printed or coerced to a string, the symbol will turn into the string
	given as its name.
]]
function Symbol.named(name)
	assert(type(name) == "string", "Symbols must be created using a string name!")

	local self = newproxy(true)

	local wrappedName = string.format("Symbol(%s)", name)

	getmetatable(self).__tostring = function()
		return wrappedName
	end

	return self
end

return Symbol


--[[
	* 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:WaitForChild('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:WaitForChild('console'))
-- 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 _G.__DEV__ then
			console.warn(
				"Attempted to access uninitialized state. Use setState to initialize state"
			)
		end
		return nil
	end,
	__newindex = function(table, key)
		if _G.__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 LuauPolyfill = require(script.Parent.Parent:WaitForChild('luau-polyfill'))
local console = LuauPolyfill.console
local consoleWithStackDev = require(script.Parent:WaitForChild('consoleWithStackDev'))

if _G.__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 LuauPolyfill = require(script.Parent.Parent:WaitForChild('luau-polyfill'))
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 LuauPolyfill = require(script.Parent:WaitForChild('luau-polyfill'))
type Object = LuauPolyfill.Object

local ReactTypes = require(script:WaitForChild('ReactTypes'))
local flowtypes = require(script:WaitForChild('flowtypes.roblox'))
local ReactElementType = require(script:WaitForChild('ReactElementType'))
local ReactFiberHostConfig = require(script:WaitForChild('ReactFiberHostConfig'))
local ReactSharedInternals = require(script:WaitForChild('ReactSharedInternals'))
local ErrorHandling = require(script:WaitForChild('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 = ReactTypes.RefObject
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:WaitForChild('checkPropTypes')),
	console = require(script:WaitForChild('console')),
	ConsolePatchingDev = require(script:WaitForChild('ConsolePatchingDev.roblox')),
	consoleWithStackDev = require(script:WaitForChild('consoleWithStackDev')),
	enqueueTask = require(script:WaitForChild('enqueueTask.roblox')),
	ExecutionEnvironment = require(script:WaitForChild('ExecutionEnvironment')),
	formatProdErrorMessage = require(script:WaitForChild('formatProdErrorMessage')),
	getComponentName = require(script:WaitForChild('getComponentName')),
	invariant = require(script:WaitForChild('invariant')),
	invokeGuardedCallbackImpl = require(script:WaitForChild('invokeGuardedCallbackImpl')),
	isValidElementType = require(script:WaitForChild('isValidElementType')),
	objectIs = require(script:WaitForChild('objectIs')),
	ReactComponentStackFrame = require(script:WaitForChild('ReactComponentStackFrame')),
	ReactElementType = require(script:WaitForChild('ReactElementType')),
	ReactErrorUtils = require(script:WaitForChild('ReactErrorUtils')),
	ReactFeatureFlags = require(script:WaitForChild('ReactFeatureFlags')),
	ReactInstanceMap = require(script:WaitForChild('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:WaitForChild('ReactSymbols')),
	ReactVersion = require(script:WaitForChild('ReactVersion')),
	shallowEqual = require(script:WaitForChild('shallowEqual')),
	UninitializedState = require(script:WaitForChild('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:WaitForChild('Symbol.roblox')),
	Type = require(script:WaitForChild('Type.roblox')),

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


local Number = require(script.Parent.Parent:WaitForChild('number'))

local NaN = Number.NaN

-- js  https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt
-- lua http://www.lua.org/manual/5.4/manual.html#pdf-utf8.codepoint
return function(str: string, index: number): number
	if type(index) ~= "number" then
		index = 1
	end
	--[[
		Initial bounds check. Checking string.len is not an exhaustive upper bound,
		but it is cheaper to check string.len and handle utf8.offset than to check
		utf.len, which iterates over all codepoints.
	]]
	local length = string.len(str)
	if index < 1 or index > length then
		return NaN
	end

	-- utf8.offset returns nil for out of bounds
	local offset = utf8.offset(str, index)

	-- check that offset is not greater than the length of the string
	if offset == nil or offset > length then
		return NaN
	end

	local value = utf8.codepoint(str, offset, offset)

	if value == nil then
		return NaN
	end

	return value
end


local function endsWith(value: string, substring: string, optionalLength: number?): boolean
	local substringLength = substring:len()
	if substringLength == 0 then
		return true
	end
	local valueLength = value:len()
	local length = optionalLength or valueLength
	if length > valueLength then
		length = valueLength
	end
	if length < 1 then
		return false
	end
	local position = length - substringLength + 1
	return value:find(substring, position, true) == position
end

return endsWith


type Match = {
	index: number,
	match: string
}

-- excluding the `+` and `*` character, since findOr tests and graphql use them explicitly
local luaPatternCharacters = "([" .. ("$%^()-[].?"):gsub("(.)", "%%%1") .. "])"

local function findOr(str: string, patternTable: { string }, initIndex: number?): Match | nil
	-- loop through all options in patern patternTable

	local init = utf8.offset(str, initIndex or 1)
	local matches = {}
	for _, value in patternTable do
		value = value:gsub(luaPatternCharacters, "%%%1")
		local iStart, iEnd = string.find(str, value, init)
		if iStart then
			local prefix = string.sub(str, 1, iStart - 1)
			local prefixEnd, invalidBytePosition = utf8.len(prefix)
			if prefixEnd == nil then
				error(("string `%s` has an invalid byte at position %s"):format(prefix, tostring(invalidBytePosition)))
			end
			local iStartIndex = prefixEnd :: number + 1
			local match = {
				index = iStartIndex,
				match = string.sub(str, iStart, iEnd),
			}
			table.insert(matches, match)
		end
	end

	-- if no matches, return nil
	if #matches == 0 then
		return nil
	end

	-- find the first matched index (after the init param)
	-- for each, if we get a hit, return the earliest index and matched term

	local firstMatch
	for _, value in matches do
		-- load first condition
		if firstMatch == nil then
			firstMatch = value
		end
		-- identify if current match comes before first match
		if value.index < firstMatch.index then
			firstMatch = value
		end
	end

	-- return first match
	return firstMatch
end

return findOr


-- excluding the `+` and `*` character, since findOr tests and graphql use them explicitly
local luaPatternCharacters = "([" .. ("$%^()-[].?"):gsub("(.)", "%%%1") .. "])"

local function includes(str: string, substring: string, position: (string | number)?): boolean
	local strLen, invalidBytePosition = utf8.len(str)
	assert(strLen ~= nil, ("string `%s` has an invalid byte at position %s"):format(str, tostring(invalidBytePosition)))
	if strLen == 0 then
		return false
	end

	if #substring == 0 then
		return true
	end

	local startIndex = 1
	if position ~= nil then
		startIndex = tonumber(position) or 1
		if startIndex > strLen then
			return false
		end
	end

	if startIndex < 1 then
		startIndex = 1
	end

	local init = utf8.offset(str, startIndex)
	local value = substring:gsub(luaPatternCharacters, "%%%1")
	local iStart, _ = string.find(str, value, init)
	return iStart ~= nil
end

return includes


-- excluding the `+` and `*` character, since findOr tests and graphql use them explicitly
local luaPatternCharacters = "([" .. ("$%^()-[].?"):gsub("(.)", "%%%1") .. "])"

-- Implements equivalent functionality to JavaScript's `String.indexOf`,
-- implementing the interface and behaviors defined at:
-- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf
return function(str: string, searchElement: string, fromIndex: number?): number
	local length = #str
	local fromIndex_ = if fromIndex ~= nil then if fromIndex < 1 then 1 else fromIndex :: number else 1

	if #searchElement == 0 then
		return if fromIndex_ > length then length else fromIndex_
	end

	if fromIndex_ > length then
		return -1
end

	searchElement = searchElement:gsub(luaPatternCharacters, "%%%1")
	local searchElementLength = #searchElement

	for i = fromIndex_, length do
		if string.sub(str, i, i + searchElementLength - 1) == searchElement then
			return i
		end
	end

	return -1
end


return {
	charCodeAt = require(script:WaitForChild('charCodeAt')),
	endsWith = require(script:WaitForChild('endsWith')),
	findOr = require(script:WaitForChild('findOr')),
	includes = require(script:WaitForChild('includes')),
	indexOf = require(script:WaitForChild('indexOf')),
	lastIndexOf = require(script:WaitForChild('lastIndexOf')),
	slice = require(script:WaitForChild('slice')),
	split = require(script:WaitForChild('split')),
	startsWith = require(script:WaitForChild('startsWith')),
	substr = require(script:WaitForChild('substr')),
	trim = require(script:WaitForChild('trim')),
	trimEnd = require(script:WaitForChild('trimEnd')),
	trimStart = require(script:WaitForChild('trimStart')),
	-- aliases for trimEnd and trimStart
	trimRight = require(script:WaitForChild('trimEnd')),
	trimLeft = require(script:WaitForChild('trimStart')),
}


local function lastIndexOf(str: string, searchValue: string, fromIndex: number?): number
	local strLength = string.len(str)
	local calculatedFromIndex
	if fromIndex then
		calculatedFromIndex = fromIndex
	else
		calculatedFromIndex = strLength
	end
	if fromIndex and fromIndex < 1 then
		calculatedFromIndex = 1
	end
	if fromIndex and fromIndex > strLength then
		calculatedFromIndex = strLength
	end
	if searchValue == "" then
		-- FIXME: Luau DFA doesn't understand that
		return calculatedFromIndex :: number
	end

	local lastFoundStartIndex, foundStartIndex
	-- Luau FIXME: Luau doesn't look beyond assignment for type, it should infer number? from loop bound
	local foundEndIndex: number? = 0
	repeat
		lastFoundStartIndex = foundStartIndex
		-- Luau FIXME: DFA doesn't understand until clause means foundEndIndex is never nil within loop
		foundStartIndex, foundEndIndex = string.find(str, searchValue, foundEndIndex :: number + 1, true)
	until foundStartIndex == nil or foundStartIndex > calculatedFromIndex

	if lastFoundStartIndex == nil then
		return -1
end
	-- Luau FIXME: Luau should see the predicate above and known the line below can only be a number
	return lastFoundStartIndex :: number
end

return lastIndexOf


local function slice(str: string, startIndexStr: string | number, lastIndexStr: (string | number)?): string
	local strLen, invalidBytePosition = utf8.len(str)
	assert(strLen ~= nil, ("string `%s` has an invalid byte at position %s"):format(str, tostring(invalidBytePosition)))
	local startIndex = tonumber(startIndexStr)
	assert(typeof(startIndex) == "number", "startIndexStr should be a number")

	if startIndex + strLen < 0 then
		-- then |start index| is greater than string length
		startIndex = 1
	end

	if startIndex > strLen then
		return ""
	end

	-- if no last index length set, go to str length + 1
	local lastIndex = strLen + 1
	if lastIndexStr ~= nil then
		-- ROBLOX FIXME: add parseInt to encapsulate this logic and use it here
		local NaN = 0/0
lastIndex = tonumber(lastIndexStr) or NaN -- this works because 0 is truthy in Lua
	end
	assert(typeof(lastIndex) == "number", "lastIndexStr should convert to number")

	if lastIndex > strLen then
		lastIndex = strLen + 1
	end

	local startIndexByte = utf8.offset(str, startIndex) :: number
	-- get char length of charset retunred at offset
	local lastIndexByte = utf8.offset(str, lastIndex) :: number - 1

	return string.sub(str, startIndexByte, lastIndexByte)
end

return slice


local findOr = require(script.Parent:WaitForChild('findOr'))
local slice = require(script.Parent:WaitForChild('slice'))

local types = require(script.Parent.Parent:WaitForChild('es7-types'))
type Array<T> = types.Array<T>
local MAX_SAFE_INTEGER = require(script.Parent.Parent:WaitForChild('number')).MAX_SAFE_INTEGER

type Pattern = string | Array<string>

local function split(str: string, _pattern: Pattern?, _limit: number?): Array<string>
	if _pattern == nil then
		return { str }
	end
	if _limit == 0 then
		return {}
	end
	local limit = if _limit == nil or _limit < 0 then MAX_SAFE_INTEGER else _limit
	local pattern = _pattern
	local patternList: Array<string>
	if typeof(pattern) == "string" then
		if pattern == "" then
			local result = {}
			for c in str:gmatch(".") do
				table.insert(result, c)
			end
			return result
		end
		patternList = { pattern }
	else
		patternList = pattern :: Array<string>
	end
	local init = 1
	local result = {}
	local lastMatch
	local strLen, invalidBytePosition = utf8.len(str)
	assert(strLen ~= nil, ("string `%s` has an invalid byte at position %s"):format(str, tostring(invalidBytePosition)))

	repeat
		local match = findOr(str, patternList, init)
		if match ~= nil then
			table.insert(result, slice(str, init, match.index))
			local matchLength = utf8.len(match.match)
			-- Luau FIXME? Luau doesn't understand that str has already been shown to be valid utf8 on line 26 and therefore won't be nil
			init = match.index + matchLength :: number
		else
			table.insert(result, slice(str, init, nil))
		end
		if match ~= nil then
			lastMatch = match
		end
	until match == nil or init > strLen or #result >= limit
	if lastMatch ~= nil then
		local lastMatchLength, invalidBytePosition_ = utf8.len(lastMatch.match)
		assert(
			lastMatchLength ~= nil,
			("string `%s` has an invalid byte at position %s"):format(lastMatch.match, tostring(invalidBytePosition_))
		)
		if lastMatch.index + lastMatchLength == strLen + 1 then
			table.insert(result, "")
		end
	end
	return result
end

return split


local function startsWith(value: string, substring: string, position: number?): boolean
	if string.len(substring) == 0 then
		return true
	end
	-- Luau FIXME: we have to use a tmp variable, as Luau doesn't understand the logic below narrow position to `number`
	local position_
	if position == nil or position < 1 then
		position_ = 1
	else
		position_ = position
	end

	if position_ > string.len(value) then
		return false
	end
	return value:find(substring, position_, true) == position_
end

return startsWith


return function(s: string, startIndex: number, numberOfCharacters: number?): string
	if numberOfCharacters and numberOfCharacters <= 0 then
		return ""
	end
	return string.sub(s, startIndex, numberOfCharacters and startIndex + numberOfCharacters - 1 or nil)
end


local trimStart = require(script.Parent:WaitForChild('trimStart'))
local trimEnd = require(script.Parent:WaitForChild('trimEnd'))

return function(source: string): string
	return trimStart(trimEnd(source))
end


return function(source: string): string
	return (source:gsub("[%s]+$", ""))
end


return function(source: string): string
	return (source:gsub("^[%s]+", ""))
end


local Symbol = require(script.Parent:WaitForChild('Symbol'))

local GlobalRegistry: { [string]: Symbol.Symbol } = {}

return {
	getOrInit = function(name: string): Symbol.Symbol
		if GlobalRegistry[name] == nil then
			GlobalRegistry[name] = Symbol.new(name)
		end

		return GlobalRegistry[name]
	end,
	-- Used for testing
	__clear = function()
		GlobalRegistry = {}
	end,
}


--!strict
--[[
	Symbols have the type 'userdata', but when printed or coerced to a string,
	the symbol will turn into the string given as its name.

	**This implementation provides only the `Symbol()` constructor and the
	global registry via `Symbol.for_`.**

	Other behaviors, including the ability to find all symbol properties on
	objects, are not implemented.
]]
export type Symbol = typeof(newproxy(true)) & { [string]: any }

return {
	new = function(name: string?): Symbol
		local self = newproxy(true) :: any

		local wrappedName = "Symbol()"
		if name then
			wrappedName = ("Symbol(%s)"):format(name)
		end

		getmetatable(self).__tostring = function()
			return wrappedName
		end

		return (self :: any) :: Symbol
	end,
}


--!strict
--[[
	A 'Symbol' is an opaque marker type, implemented to behave similarly to JS:
	https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol
]]
local Symbol = require(script:WaitForChild('Symbol'))
export type Symbol = Symbol.Symbol
local GlobalRegistry = require(script:WaitForChild('Registry.global'))

local SymbolObject = setmetatable({}, {
	--[[
		Creates a new symbol, using the given name when printed. Symbols are
		opaque, so this will always create a new, unique object
	]]
	__call = function(_, name: string?): Symbol.Symbol
		return Symbol.new(name)
	end,
})

SymbolObject.for_ = GlobalRegistry.getOrInit

return SymbolObject


local Object = require(script.Parent:WaitForChild('collections')).Object

local makeTimerImpl = require(script:WaitForChild('makeTimerImpl'))
local makeIntervalImpl = require(script:WaitForChild('makeIntervalImpl'))

export type Timeout = makeTimerImpl.Timeout
export type Interval = makeIntervalImpl.Interval

return Object.assign({}, makeTimerImpl(task.delay), makeIntervalImpl(task.delay))