diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 05ae876..0000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -patreon: erynlynn diff --git a/.gitignore b/.gitignore index 82afbfe..edbcfff 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ node_modules/ .vscode build -roblox.toml \ No newline at end of file +roblox.toml +sourcemap.json \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index b112a89..c5657e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## FORKED VERSION [4.0.0] +- Added `any` type annotations to many variables in the internal implementation in order to prevent crashes while using this library +- Added `--!nonstrict` compiler directive at the top of the script to expose types +- Added library-user-facing typings for `Promise`, `Error`, `Status` and the library itself. This fork uses the `(object :: any) :: PublicType` idiom to simplify the types to the library, allowing for better autocompletion and type annotations in a strict-mode Luau codebase + +No other changes have been made to the library's runtime behavior. + +Please notify me through the issues section if any issues are encountered related to type safety or crashes. Otherwise, please send any issues related to runtime behavior to the [original repository](https://github.com/evaera/roblox-lua-promise). + ## [4.0.0] ### Changed - `Promise:finally` no longer observes a rejection from a Promise. Calling `Promise:finally` is mostly transparent now. diff --git a/LICENSE b/LICENSE index acf72c8..e73391f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,6 @@ MIT License +Copyright (c) 2022 Amber Grace Copyright (c) 2019 Eryn L. K. Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/README.md b/README.md index 48233f8..70877c6 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,10 @@
-

Roblox Lua Promise

+

Roblox Luau Promise

An implementation of Promise similar to Promise/A+.

View docs
- ## Why you should use Promises The way Roblox models asynchronous operations by default is by yielding (stopping) the thread and then resuming it when the future value is available. This model is not ideal because: @@ -20,3 +19,9 @@ This Promise implementation attempts to satisfy these traits: * An object that represents a unit of asynchronous work * Composability * Predictable timing + +## FORKED VERSION + +This is a forked version of [Evaera's Promise library](https://github.com/evaera/roblox-lua-promise) with type annotations added to allow this library to be used in a strict-mode [Luau](https://luau-lang.org) project. Type annotations are imperfect due to limitations with the Luau language, but should at least cover intellisense and basic static analysis. + +Please notify me through the issues section if any issues are encountered related to type safety or crashes. Otherwise, please send any issues related to runtime behavior to the [original repository](https://github.com/evaera/roblox-lua-promise). \ No newline at end of file diff --git a/foreman.toml b/aftman.toml similarity index 100% rename from foreman.toml rename to aftman.toml diff --git a/lib/LICENSE.luau b/lib/LICENSE.luau new file mode 100644 index 0000000..19647f0 --- /dev/null +++ b/lib/LICENSE.luau @@ -0,0 +1,26 @@ +return [[ + +MIT License + +Copyright (c) 2022 Amber Grace +Copyright (c) 2019 Eryn L. K. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +]] \ No newline at end of file diff --git a/lib/init.lua b/lib/init.luau similarity index 88% rename from lib/init.lua rename to lib/init.luau index 97cdf37..90195b3 100644 --- a/lib/init.lua +++ b/lib/init.luau @@ -1,13 +1,144 @@ +--!nonstrict --[[ - An implementation of Promises similar to Promise/A+. + An implementation of Promises similar to Promise/A+ + + Original library by Eryn L. K. + Edit by Amber Grace (@DataBrain) to include custom type annotations & extensions + Forked from github repository: evaera/roblox-lua-promise + Forked from release V4.0.0 (Mar 2nd 2022). + + I do not intend to maintain + this forked version of the library unless a major bug or exploit is + found with the original library that needs to be patched here. + + Licensed under MIT License (see nested module for full license) ]] +export type Status = "Started" | "Resolved" | "Rejected" | "Cancelled" +export type Executor = ( + resolve: (...any) -> (), + reject: (...any) -> (), + onCancel: (abortHandler: (() -> ())?) -> boolean +) -> () +export type Promise = { + timeout: (self: Promise, seconds: number, rejectionValue: any?) -> Promise, + getStatus: (self: Promise) -> Status, + andThen: ( + self: Promise, + successHandler: (...any) -> ...any, + failureHandler: ((...any) -> ...any)? + ) -> Promise, + catch: ( + self: Promise, + failureHandler: (...any) -> ...any + ) -> Promise, + tap: ( + self: Promise, + tapHandler: (...any) -> ...any + ) -> Promise, + andThenCall: ( + self: Promise, + callback: (T...) -> any, + T... + ) -> Promise, + andThenReturn: ( + self: Promise, + ...any + ) -> Promise, + cancel: (self: Promise) -> (), + finally: ( + self: Promise, + finallyHandler: (status: Status) -> ...any + ) -> Promise, + finallyCall: ( + self: Promise, + callback: (T...) -> any, + T... + ) -> Promise, + finallyReturn: ( + self: Promise, + ...any + ) -> Promise, + awaitStatus: ( + self: Promise + ) -> (Status, ...any), + await: (self: Promise) -> (boolean, ...any), + expect: (self: Promise) -> ...any, + now: (self: Promise, rejectionValue: any?) -> Promise, +} +export type ErrorKind = "ExecutionError" | "AlreadyCancelled" | "NotResolvedInTime" | "TimedOut" +export type ErrorOptions = { + error: string?, + trace: string?, + context: string?, + kind: ErrorKind +} +export type Error = { + kind: ErrorKind, + trace: string?, + context: string?, + parent: Error?, + error: string, + createdTick: number, + createdTrace: string, + + extend: (self: Error, options: ErrorOptions) -> Error, + getErrorChain: (self: Error) -> {Error}, +} +type PromiseLib = { + new: (executor: Executor) -> Promise, + defer: (executor: Executor) -> Promise, + resolve: (...any) -> Promise, + reject: (...any) -> Promise, + try: ( + callback: (T...) -> ...any, + T... + ) -> Promise, + all: (promises: {Promise}) -> Promise, + fold: ( + list: {any}, + reducer: (accumulator: any, value: any, index: number) -> any, + initialValue: any + ) -> (), + some: (promises: {Promise}, count: number) -> Promise, + any: (promises: {Promise}) -> Promise, + allSettled: (promises: {Promise}) -> Promise, + race: (promises: {Promise}) -> Promise, + each: ( + list: {any}, + predicate: (value: any, index: number) -> any + ) -> Promise, + is: (object: any) -> boolean, + promisify: ( + callback: (T...) -> ...any + ) -> ((T...) -> Promise), + delay: (seconds: number) -> Promise, + retry: ( + callback: (P...) -> Promise, + times: number, + P... + ) -> Promise, + retryWithDelay: ( + callback: (P...) -> Promise, + times: number, + seconds: number, + P... + ) -> Promise, + fromEvent: ( + event: RBXScriptSignal | {Connect: any}, + predicate: ((...any) -> boolean)? + ) -> Promise, + onUnhandledRejection: ( + callback: (Promise, ...any) -> () + ) -> (() -> ()) +} + local ERROR_NON_PROMISE_IN_LIST = "Non-promise value passed into %s at index %s" local ERROR_NON_LIST = "Please pass a list of promises to %s" local ERROR_NON_FUNCTION = "Please pass a handler function to %s!" -local MODE_KEY_METATABLE = { __mode = "k" } +local MODE_KEY_METATABLE: any = { __mode = "k" } -local function isCallable(value) +local function isCallable(value: any) if type(value) == "function" then return true end @@ -25,7 +156,7 @@ end --[[ Creates an enum dictionary with some metamethods to prevent common mistakes. ]] -local function makeEnum(enumName, members) +local function makeEnum(enumName: any, members: any) local enum = {} for _, memberName in ipairs(members) do @@ -49,7 +180,7 @@ end @class Error ]=] -local Error +local Error: any do Error = { Kind = makeEnum("Promise.Error.Kind", { @@ -61,7 +192,7 @@ do } Error.__index = Error - function Error.new(options, parent) + function Error.new(options: any, parent: any) options = options or {} return setmetatable({ error = tostring(options.error) or "[This error has no error text.]", @@ -74,7 +205,7 @@ do }, Error) end - function Error.is(anything) + function Error.is(anything: any) if type(anything) == "table" then local metatable = getmetatable(anything) @@ -86,13 +217,13 @@ do return false end - function Error.isKind(anything, kind) + function Error.isKind(anything: any, kind: any) assert(kind ~= nil, "Argument #2 to Promise.Error.isKind must not be nil") return Error.is(anything) and anything.kind == kind end - function Error:extend(options) + function Error:extend(options: any) options = options or {} options.kind = options.kind or self.kind @@ -134,21 +265,21 @@ end Used to cajole varargs without dropping sparse values. ]] -local function pack(...) +local function pack(...: any) return select("#", ...), { ... } end --[[ Returns first value (success), and packs all following values. ]] -local function packResult(success, ...) +local function packResult(success: any, ...: any) return success, select("#", ...), { ... } end -local function makeErrorHandler(traceback) +local function makeErrorHandler(traceback: any) assert(traceback ~= nil, "traceback is nil") - return function(err) + return function(err: any) -- If the error object is already a table, forward it directly. -- Should we extend the error here and add our own trace? @@ -168,7 +299,7 @@ end --[[ Calls a Promise executor with error handling. ]] -local function runExecutor(traceback, callback, ...) +local function runExecutor(traceback: any, callback: any, ...: any) return packResult(xpcall(callback, makeErrorHandler(traceback), ...)) end @@ -176,8 +307,8 @@ end Creates a function that invokes a callback with correct error handling and resolution mechanisms. ]] -local function createAdvancer(traceback, callback, resolve, reject) - return function(...) +local function createAdvancer(traceback: any, callback: any, resolve: any, reject: any) + return function(...: any) local ok, resultLength, result = runExecutor(traceback, callback, ...) if ok then @@ -188,7 +319,7 @@ local function createAdvancer(traceback, callback, resolve, reject) end end -local function isEmpty(t) +local function isEmpty(t: any) return next(t) == nil end @@ -217,22 +348,23 @@ end @class Promise @__index prototype ]=] -local Promise = { +local Promise: any = { Error = Error, Status = makeEnum("Promise.Status", { "Started", "Resolved", "Rejected", "Cancelled" }), _getTime = os.clock, _timeEvent = game:GetService("RunService").Heartbeat, _unhandledRejectionCallbacks = {}, + TEST = nil :: boolean?, } Promise.prototype = {} Promise.__index = Promise.prototype -function Promise._new(traceback, callback, parent) +function Promise._new(traceback: any, callback: any, parent: any) if parent ~= nil and not Promise.is(parent) then error("Argument #2 to Promise.new must be a promise or nil", 2) end - local self = { + local self: any = { -- The executor thread. _thread = nil, @@ -275,15 +407,15 @@ function Promise._new(traceback, callback, parent) setmetatable(self, Promise) - local function resolve(...) + local function resolve(...: any) self:_resolve(...) end - local function reject(...) + local function reject(...: any) self:_reject(...) end - local function onCancel(cancellationHook) + local function onCancel(cancellationHook: any) if cancellationHook then if self._status == Promise.Status.Cancelled then cancellationHook() @@ -346,7 +478,7 @@ end @param executor (resolve: (...: any) -> (), reject: (...: any) -> (), onCancel: (abortHandler?: () -> ()) -> boolean) -> () @return Promise ]=] -function Promise.new(executor) +function Promise.new(executor: any) return Promise._new(debug.traceback(nil, 2), executor) end @@ -372,7 +504,7 @@ end @param executor (resolve: (...: any) -> (), reject: (...: any) -> (), onCancel: (abortHandler?: () -> ()) -> boolean) -> () @return Promise ]=] -function Promise.defer(executor) +function Promise.defer(executor: any) local traceback = debug.traceback(nil, 2) local promise promise = Promise._new(traceback, function(resolve, reject, onCancel) @@ -415,7 +547,7 @@ Promise.async = Promise.defer @param ... any @return Promise<...any> ]=] -function Promise.resolve(...) +function Promise.resolve(...: any) local length, values = pack(...) return Promise._new(debug.traceback(nil, 2), function(resolve) resolve(unpack(values, 1, length)) @@ -432,7 +564,7 @@ end @param ... any @return Promise<...any> ]=] -function Promise.reject(...) +function Promise.reject(...: any) local length, values = pack(...) return Promise._new(debug.traceback(nil, 2), function(_, reject) reject(unpack(values, 1, length)) @@ -443,7 +575,7 @@ end Runs a non-promise-returning function as a Promise with the given arguments. ]] -function Promise._try(traceback, callback, ...) +function Promise._try(traceback: any, callback: any, ...: any) local valuesLength, values = pack(...) return Promise._new(traceback, function(resolve) @@ -474,7 +606,7 @@ end @param ... T... -- Additional arguments passed to `callback` @return Promise ]=] -function Promise.try(callback, ...) +function Promise.try(callback: any, ...: any) return Promise._try(debug.traceback(nil, 2), callback, ...) end @@ -483,7 +615,7 @@ end * is resolved when all input promises resolve * is rejected if ANY input promises reject ]] -function Promise._all(traceback, promises, amount) +function Promise._all(traceback: any, promises: any, amount: any) if type(promises) ~= "table" then error(string.format(ERROR_NON_LIST, "Promise.all"), 3) end @@ -588,7 +720,7 @@ end @param promises {Promise} @return Promise<{T}> ]=] -function Promise.all(promises) +function Promise.all(promises: any) return Promise._all(debug.traceback(nil, 2), promises) end @@ -617,7 +749,7 @@ end @param reducer (accumulator: U, value: T, index: number) -> U | Promise @param initialValue U ]=] -function Promise.fold(list, reducer, initialValue) +function Promise.fold(list: any, reducer: any, initialValue: any) assert(type(list) == "table", "Bad argument #1 to Promise.fold: must be a table") assert(isCallable(reducer), "Bad argument #2 to Promise.fold: must be a function") @@ -650,7 +782,7 @@ end @param count number @return Promise<{T}> ]=] -function Promise.some(promises, count) +function Promise.some(promises: any, count: any) assert(type(count) == "number", "Bad argument #2 to Promise.some: must be a number") return Promise._all(debug.traceback(nil, 2), promises, count) @@ -674,7 +806,7 @@ end @param promises {Promise} @return Promise ]=] -function Promise.any(promises) +function Promise.any(promises: {any}) return Promise._all(debug.traceback(nil, 2), promises, 1):andThen(function(values) return values[1] end) @@ -696,7 +828,7 @@ end @param promises {Promise} @return Promise<{Status}> ]=] -function Promise.allSettled(promises) +function Promise.allSettled(promises: any) if type(promises) ~= "table" then error(string.format(ERROR_NON_LIST, "Promise.allSettled"), 2) end @@ -774,7 +906,7 @@ end @param promises {Promise} @return Promise ]=] -function Promise.race(promises) +function Promise.race(promises: any) assert(type(promises) == "table", string.format(ERROR_NON_LIST, "Promise.race")) for i, promise in pairs(promises) do @@ -869,7 +1001,7 @@ end @param predicate (value: T, index: number) -> U | Promise @return Promise<{U}> ]=] -function Promise.each(list, predicate) +function Promise.each(list: any, predicate: any) assert(type(list) == "table", string.format(ERROR_NON_LIST, "Promise.each")) assert(isCallable(predicate), string.format(ERROR_NON_FUNCTION, "Promise.each")) @@ -959,6 +1091,7 @@ function Promise.each(list, predicate) end resolve(results) + return end) end @@ -968,7 +1101,7 @@ end @param object any @return boolean -- `true` if the given `object` is a Promise. ]=] -function Promise.is(object) +function Promise.is(object: any) if type(object) ~= "table" then return false end @@ -1017,8 +1150,8 @@ end @param callback (...: any) -> ...any @return (...: any) -> Promise ]=] -function Promise.promisify(callback) - return function(...) +function Promise.promisify(callback: any) + return function(...: any) return Promise._try(debug.traceback(nil, 2), callback, ...) end end @@ -1048,7 +1181,7 @@ do local first local connection - function Promise.delay(seconds) + function Promise.delay(seconds: any) assert(type(seconds) == "number", "Bad argument #1 to Promise.delay, must be a number.") -- If seconds is -INF, INF, NaN, or less than 1 / 60, assume seconds is 1 / 60. -- This mirrors the behavior of wait() @@ -1056,7 +1189,7 @@ do seconds = 1 / 60 end - return Promise._new(debug.traceback(nil, 2), function(resolve, _, onCancel) + return Promise._new(debug.traceback(nil, 2), function(resolve: any, _: any, onCancel: any) local startTime = Promise._getTime() local endTime = startTime + seconds @@ -1177,7 +1310,7 @@ end @param rejectionValue? any -- The value to reject with if the timeout is reached @return Promise ]=] -function Promise.prototype:timeout(seconds, rejectionValue) +function Promise.prototype:timeout(seconds: any, rejectionValue: any) local traceback = debug.traceback(nil, 2) return Promise.race({ @@ -1210,7 +1343,7 @@ end The given callbacks are invoked depending on that result. ]] -function Promise.prototype:_andThen(traceback, successHandler, failureHandler) +function Promise.prototype:_andThen(traceback: any, successHandler: any, failureHandler: any) self._unhandledRejection = false -- If we are already cancelled, we return a cancelled Promise @@ -1222,7 +1355,7 @@ function Promise.prototype:_andThen(traceback, successHandler, failureHandler) end -- Create a new promise to follow this part of the chain - return Promise._new(traceback, function(resolve, reject, onCancel) + return Promise._new(traceback, function(resolve: any, reject: any, onCancel: any) -- Our default callbacks just pass values onto the next promise. -- This lets success and failure cascade correctly! @@ -1280,7 +1413,7 @@ end @param failureHandler? (...: any) -> ...any @return Promise<...any> ]=] -function Promise.prototype:andThen(successHandler, failureHandler) +function Promise.prototype:andThen(successHandler: any, failureHandler: any) assert(successHandler == nil or isCallable(successHandler), string.format(ERROR_NON_FUNCTION, "Promise:andThen")) assert(failureHandler == nil or isCallable(failureHandler), string.format(ERROR_NON_FUNCTION, "Promise:andThen")) @@ -1307,7 +1440,7 @@ end @param failureHandler (...: any) -> ...any @return Promise<...any> ]=] -function Promise.prototype:catch(failureHandler) +function Promise.prototype:catch(failureHandler: any) assert(failureHandler == nil or isCallable(failureHandler), string.format(ERROR_NON_FUNCTION, "Promise:catch")) return self:_andThen(debug.traceback(nil, 2), nil, failureHandler) end @@ -1328,9 +1461,9 @@ end @param tapHandler (...: any) -> ...any @return Promise<...any> ]=] -function Promise.prototype:tap(tapHandler) +function Promise.prototype:tap(tapHandler: any) assert(isCallable(tapHandler), string.format(ERROR_NON_FUNCTION, "Promise:tap")) - return self:_andThen(debug.traceback(nil, 2), function(...) + return self:_andThen(debug.traceback(nil, 2), function(...: any) local callbackReturn = tapHandler(...) if Promise.is(callbackReturn) then @@ -1363,7 +1496,7 @@ end @param ...? any -- Additional arguments which will be passed to `callback` @return Promise ]=] -function Promise.prototype:andThenCall(callback, ...) +function Promise.prototype:andThenCall(callback: any, ...: any) assert(isCallable(callback), string.format(ERROR_NON_FUNCTION, "Promise:andThenCall")) local length, values = pack(...) return self:_andThen(debug.traceback(nil, 2), function() @@ -1393,7 +1526,7 @@ end @param ... any -- Values to return from the function @return Promise ]=] -function Promise.prototype:andThenReturn(...) +function Promise.prototype:andThenReturn(...: any) local length, values = pack(...) return self:_andThen(debug.traceback(nil, 2), function() return unpack(values, 1, length) @@ -1439,7 +1572,7 @@ end Used to decrease the number of consumers by 1, and if there are no more, cancel this promise. ]] -function Promise.prototype:_consumerCancelled(consumer) +function Promise.prototype:_consumerCancelled(consumer: any) if self._status ~= Promise.Status.Started then return end @@ -1455,10 +1588,10 @@ end Used to set a handler for when the promise resolves, rejects, or is cancelled. ]] -function Promise.prototype:_finally(traceback, finallyHandler) +function Promise.prototype:_finally(traceback: any, finallyHandler: any) self._unhandledRejection = false - local promise = Promise._new(traceback, function(resolve, reject, onCancel) + local promise = Promise._new(traceback, function(resolve: any, reject: any, onCancel: any) local handlerPromise onCancel(function() @@ -1474,7 +1607,7 @@ function Promise.prototype:_finally(traceback, finallyHandler) local finallyCallback = resolve if finallyHandler then - finallyCallback = function(...) + finallyCallback = function(...: any) local callbackReturn = finallyHandler(...) if Promise.is(callbackReturn) then @@ -1486,7 +1619,7 @@ function Promise.prototype:_finally(traceback, finallyHandler) resolve(self) end end) - :catch(function(...) + :catch(function(...: any) reject(...) end) else @@ -1556,7 +1689,7 @@ end @param finallyHandler (status: Status) -> ...any @return Promise<...any> ]=] -function Promise.prototype:finally(finallyHandler) +function Promise.prototype:finally(finallyHandler: any) assert(finallyHandler == nil or isCallable(finallyHandler), string.format(ERROR_NON_FUNCTION, "Promise:finally")) return self:_finally(debug.traceback(nil, 2), finallyHandler) end @@ -1570,7 +1703,7 @@ end @param ...? any -- Additional arguments which will be passed to `callback` @return Promise ]=] -function Promise.prototype:finallyCall(callback, ...) +function Promise.prototype:finallyCall(callback: any, ...: any) assert(isCallable(callback), string.format(ERROR_NON_FUNCTION, "Promise:finallyCall")) local length, values = pack(...) return self:_finally(debug.traceback(nil, 2), function() @@ -1596,7 +1729,7 @@ end @param ... any -- Values to return from the function @return Promise ]=] -function Promise.prototype:finallyReturn(...) +function Promise.prototype:finallyReturn(...: any) local length, values = pack(...) return self:_finally(debug.traceback(nil, 2), function() return unpack(values, 1, length) @@ -1638,7 +1771,7 @@ function Promise.prototype:awaitStatus() return self._status end -local function awaitHelper(status, ...) +local function awaitHelper(status: any, ...: any) return status == Promise.Status.Resolved, ... end @@ -1667,7 +1800,7 @@ function Promise.prototype:await() return awaitHelper(self:awaitStatus()) end -local function expectHelper(status, ...) +local function expectHelper(status: any, ...: any) if status ~= Promise.Status.Resolved then error((...) == nil and "Expected Promise rejected with no value." or (...), 3) end @@ -1724,7 +1857,7 @@ function Promise.prototype:_unwrap() return success, unpack(self._values, 1, self._valuesLength) end -function Promise.prototype:_resolve(...) +function Promise.prototype:_resolve(...: any) if self._status ~= Promise.Status.Started then if Promise.is((...)) then (...):_consumerCancelled(self) @@ -1745,9 +1878,9 @@ function Promise.prototype:_resolve(...) local chainedPromise = ... - local promise = chainedPromise:andThen(function(...) + local promise = chainedPromise:andThen(function(...: any) self:_resolve(...) - end, function(...) + end, function(...: any) local maybeRuntimeError = chainedPromise._values[1] -- Backwards compatibility < v2 @@ -1771,6 +1904,7 @@ function Promise.prototype:_resolve(...) end self:_reject(...) + return end) if promise._status == Promise.Status.Cancelled then @@ -1795,7 +1929,7 @@ function Promise.prototype:_resolve(...) self:_finalize() end -function Promise.prototype:_reject(...) +function Promise.prototype:_reject(...: any) if self._status ~= Promise.Status.Started then return end @@ -1886,10 +2020,10 @@ end @param rejectionValue? any -- The value to reject with if the Promise isn't resolved @return Promise ]=] -function Promise.prototype:now(rejectionValue) +function Promise.prototype:now(rejectionValue: any) local traceback = debug.traceback(nil, 2) if self._status == Promise.Status.Resolved then - return self:_andThen(traceback, function(...) + return self:_andThen(traceback, function(...: any) return ... end) else @@ -1931,13 +2065,13 @@ end @param ...? P @return Promise ]=] -function Promise.retry(callback, times, ...) +function Promise.retry(callback: any, times: any, ...: any) assert(isCallable(callback), "Parameter #1 to Promise.retry must be a function") assert(type(times) == "number", "Parameter #2 to Promise.retry must be a number") local args, length = { ... }, select("#", ...) - return Promise.resolve(callback(...)):catch(function(...) + return Promise.resolve(callback(...)):catch(function(...: any) if times > 0 then return Promise.retry(callback, times - 1, unpack(args, 1, length)) else @@ -1959,14 +2093,14 @@ end @param ...? P @return Promise ]=] -function Promise.retryWithDelay(callback, times, seconds, ...) +function Promise.retryWithDelay(callback: any, times: any, seconds: any, ...: any) assert(isCallable(callback), "Parameter #1 to Promise.retry must be a function") assert(type(times) == "number", "Parameter #2 (times) to Promise.retry must be a number") assert(type(seconds) == "number", "Parameter #3 (seconds) to Promise.retry must be a number") local args, length = { ... }, select("#", ...) - return Promise.resolve(callback(...)):catch(function(...) + return Promise.resolve(callback(...)):catch(function(...: any) if times > 0 then Promise.delay(seconds):await() @@ -2001,12 +2135,12 @@ end @param predicate? (...: P) -> boolean -- A function which determines if the Promise should resolve with the given value, or wait for the next event to check again. @return Promise

]=] -function Promise.fromEvent(event, predicate) +function Promise.fromEvent(event: any, predicate: any) predicate = predicate or function() return true end - return Promise._new(debug.traceback(nil, 2), function(resolve, _, onCancel) + return Promise._new(debug.traceback(nil, 2), function(resolve: any, _: any, onCancel: any) local connection local shouldDisconnect = false @@ -2019,8 +2153,8 @@ function Promise.fromEvent(event, predicate) -- Connect returns, connection will still be nil. This happens with events that queue up -- events when there's nothing connected, such as RemoteEvents - connection = event:Connect(function(...) - local callbackValue = predicate(...) + connection = event:Connect(function(...: any) + local callbackValue = (predicate :: any)(...) if callbackValue == true then resolve(...) @@ -2040,6 +2174,7 @@ function Promise.fromEvent(event, predicate) end onCancel(disconnect) + return end) end @@ -2053,7 +2188,7 @@ end @param callback (promise: Promise, ...: any) -- A callback that runs when an unhandled rejection happens. @return () -> () -- Function that unregisters the `callback` when called ]=] -function Promise.onUnhandledRejection(callback) +function Promise.onUnhandledRejection(callback: any) table.insert(Promise._unhandledRejectionCallbacks, callback) return function() @@ -2065,4 +2200,4 @@ function Promise.onUnhandledRejection(callback) end end -return Promise +return (Promise :: any) :: PromiseLib diff --git a/lib/init.spec.lua b/lib/init.spec.luau similarity index 100% rename from lib/init.spec.lua rename to lib/init.spec.luau diff --git a/rotriever.toml b/rotriever.toml deleted file mode 100644 index b105f14..0000000 --- a/rotriever.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "evaera/roblox-lua-promise" -version = "4.0.0" -author = "evaera" -content_root = "lib" -license = "MIT" - -[dependencies] diff --git a/selene.toml b/selene.toml deleted file mode 100644 index c1911be..0000000 --- a/selene.toml +++ /dev/null @@ -1 +0,0 @@ -std = "roblox+testez" \ No newline at end of file diff --git a/wally.toml b/wally.toml index 4d85244..49607de 100644 --- a/wally.toml +++ b/wally.toml @@ -1,6 +1,6 @@ [package] -name = "evaera/promise" -description = "Promise implementation for Roblox" +name = "ambers-careware/promise" +description = "Promise implementation for Roblox Luau" version = "4.0.0" license = "MIT" registry = "https://github.com/UpliftGames/wally-index"