local C=game:GetService("\082\117\110\083\101\114\118\105\099\101")local K=nil local y={}local E={}local t={}function t.addFutureRejectionHandler(C)assert(type(C)=="\102\117\110\099\116\105\111\110","\070\117\116\117\114\101 \114\101\106\101\099\116\105\111\110 \104\097\110\100\108\101\114 \109\117\115\116 \098\101 \097 \102\117\110\099\116\105\111\110")local K={[C]=true}for C in pairs(y)do K[C]=true end y=K return function()local K={}for y in pairs(y)do if y~=C then K[y]=true end end y=K end end function t.exec(C)local K=Instance.new("\066\105\110\100\097\098\108\101\069\118\101\110\116")K.Event:Connect(C)K:Fire()K:Destroy()end function t.__start()if K~=nil then return end K=C.Stepped:Connect(function()if next(E)==nil and K~=nil then K:Disconnect()K=nil return end local C={}for K in pairs(E)do local y=coroutine.status(K.co)if y=="\100\101\097\100"then if K.completed then else local C=debug.traceback(K.co)t.__fireFutureRejection(C)end else C[K]=true end end E=C end)end function t.__runFutureListener(C,K)local y={completed=false;co=nil}y.co=coroutine.create(function(...)C(...)y.completed=true end)local m,S=coroutine.resume(y.co,K)if not m then local C=debug.traceback(y.co,tostring(S))t.__fireFutureRejection(C)return end if coroutine.status(y.co)=="\115\117\115\112\101\110\100\101\100"then E[y]=true t.__start()end end function t.__fireFutureRejection(C)local K=y local E=Instance.new("\066\105\110\100\097\098\108\101\069\118\101\110\116")if next(K)~=nil then for C in pairs(K)do local K=E.Event:Connect(C)E:Fire()K:Disconnect()end else E.Event:Connect(function()error(string.format("\070\117\116\117\114\101 \101\114\114\111\114\058 \037\115",C),0)end)E:Fire()end E:Destroy()end return t
local RunService = game:GetService("RunService")

local pollConnection = nil
local rejectionHandlers = {}
local trackedTasks = {}

local Runtime = {}

function Runtime.addFutureRejectionHandler(handler)
	assert(type(handler) == "function", "Future rejection handler must be a function")

	local newHandlers = {
		[handler] = true
	}

	for existing in pairs(rejectionHandlers) do
		newHandlers[existing] = true
	end

	rejectionHandlers = newHandlers

	return function()
		local newHandlers = {}

		for existing in pairs(rejectionHandlers) do
			if existing ~= handler then
				newHandlers[existing] = true
			end
		end

		rejectionHandlers = newHandlers
	end
end

-- Alternative to `spawn` or `coroutine.wrap` that runs immediately, but
-- propagates errors to the engine instead of swallowing them or killing the
-- containing thread.
function Runtime.exec(functor)
	local bindable = Instance.new("BindableEvent")
	bindable.Event:Connect(functor)
	bindable:Fire()
	bindable:Destroy()
end

-- Start the Asink runtime if it isn't already running.
--
-- The runtime will monitor outstanding tasks that yielded at some point and
-- watch them for errors. It isn't strictly necessary for the happy path, but is
-- necessary to let Lua code know about fatal errors in Futures.
function Runtime.__start()
	if pollConnection ~= nil then
		return
	end

	pollConnection = RunService.Stepped:Connect(function()
		if next(trackedTasks) == nil and pollConnection ~= nil then
			pollConnection:Disconnect()
			pollConnection = nil
			return
		end

		local nextTaskSet = {}

		for task in pairs(trackedTasks) do
			local status = coroutine.status(task.co)

			if status == "dead" then
				if task.completed then
					-- This task completed without a hitch.
				else
					-- This task died a gruesome death.
					-- Someone else will have received an error.
					local message = debug.traceback(task.co)
					Runtime.__fireFutureRejection(message)
				end
			else
				-- This task is still outstanding, carry it over.
				nextTaskSet[task] = true
			end
		end

		trackedTasks = nextTaskSet
	end)
end

function Runtime.__runFutureListener(listener, value)
	local task = {
		completed = false,
		co = nil,
	}

	task.co = coroutine.create(function(...)
		listener(...)
		task.completed = true
	end)
	local success, result = coroutine.resume(task.co, value)

	if not success then
		local message = debug.traceback(task.co, tostring(result))
		Runtime.__fireFutureRejection(message)
		return
	end

	-- If the listener yielded, we'll track the task to monitor it for errors.
	if coroutine.status(task.co) == "suspended" then
		trackedTasks[task] = true
		Runtime.__start()
	end
end

function Runtime.__fireFutureRejection(errorMessage)
	local handlers = rejectionHandlers

	local bindable = Instance.new("BindableEvent")

	if next(handlers) ~= nil then
		for handler in pairs(handlers) do
			local connection = bindable.Event:Connect(handler)
			bindable:Fire()
			connection:Disconnect()
		end
	else
		bindable.Event:Connect(function()
			error(string.format("Future error: %s", errorMessage), 0)
		end)
		bindable:Fire()
	end

	bindable:Destroy()
end

return Runtime