@quenty/promise 10.22.0 → 10.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,16 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [10.23.1](https://github.com/Quenty/NevermoreEngine/compare/@quenty/promise@10.23.0...@quenty/promise@10.23.1) (2026-08-14)
7
+
8
+ **Note:** Version bump only for package @quenty/promise
9
+
10
+ # [10.23.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/promise@10.22.0...@quenty/promise@10.23.0) (2026-07-25)
11
+
12
+ ### Features
13
+
14
+ - Promises don't reject on Promise.rejected(nil) ([be63400](https://github.com/Quenty/NevermoreEngine/commit/be63400f89c863624b75b1903f7e8357ddca7bf9))
15
+
6
16
  # [10.22.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/promise@10.21.0...@quenty/promise@10.22.0) (2026-07-23)
7
17
 
8
18
  ### Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quenty/promise",
3
- "version": "10.22.0",
3
+ "version": "10.23.1",
4
4
  "description": "Promise implementation for Roblox",
5
5
  "keywords": [
6
6
  "Roblox",
@@ -29,15 +29,15 @@
29
29
  ],
30
30
  "dependencies": {
31
31
  "@quenty/deferred": "2.3.2",
32
- "@quenty/loader": "10.11.0",
33
- "@quenty/maid": "3.11.0",
32
+ "@quenty/loader": "10.11.1",
33
+ "@quenty/maid": "3.11.1",
34
34
  "@quenty/math": "2.7.5",
35
- "@quenty/nevermore-test-runner": "1.5.0",
36
- "@quenty/signal": "7.13.1",
35
+ "@quenty/nevermore-test-runner": "1.5.1",
36
+ "@quenty/signal": "7.13.2",
37
37
  "@quentystudios/jest-lua": "3.10.0-quenty.2"
38
38
  },
39
39
  "publishConfig": {
40
40
  "access": "public"
41
41
  },
42
- "gitHead": "88b03743718172ff79cd62ade64a86565fcac17d"
42
+ "gitHead": "f084360c3729ea424d49d9bc42f8f0d1c128fa82"
43
43
  }
@@ -363,11 +363,8 @@ function Promise._reject<T...>(self: Promise<T...>, values: { n: number, [number
363
363
  self:_executeThen(unpack(data))
364
364
  end
365
365
 
366
- -- Check for uncaught exceptions. A single empty table carries no more information than an
367
- -- empty rejection (aggregators may reject with an empty results table), so it is not
368
- -- reported either.
369
- local isEmptyTableRejection = values.n == 1 and type(values[1]) == "table" and next(values[1]) == nil
370
- if self._unconsumedException and values.n > 0 and not isEmptyTableRejection then
366
+ -- Check for uncaught exceptions
367
+ if self._unconsumedException and self:_isReportableRejection(values) then
371
368
  task.defer(function()
372
369
  -- Yield to end of frame, giving control back to Roblox.
373
370
  -- This is the equivalent of giving something back to a task manager.
@@ -386,6 +383,23 @@ function Promise._reject<T...>(self: Promise<T...>, values: { n: number, [number
386
383
  end
387
384
  end
388
385
 
386
+ -- A rejection is only worth reporting if it carries information. Rejecting with no values at all
387
+ -- (what Destroy() and cancellation produce), with values that are all nil, or with a single empty
388
+ -- table (aggregators may reject with an empty results table) says nothing a warning could relay.
389
+ function Promise._isReportableRejection<T...>(_self: Promise<T...>, values: { n: number, [number]: any }): boolean
390
+ if values.n == 1 and type(values[1]) == "table" and next(values[1]) == nil then
391
+ return false
392
+ end
393
+
394
+ for index = 1, values.n do
395
+ if values[index] ~= nil then
396
+ return true
397
+ end
398
+ end
399
+
400
+ return false
401
+ end
402
+
389
403
  function Promise._toHumanReadable<T...>(_self: Promise<T...>, data: any): string
390
404
  if type(data) == "table" then
391
405
  -- A custom __tostring is the best human-readable form; only address-form tables
@@ -133,6 +133,25 @@ describe("Promise.rejected", function()
133
133
  it("reuses a single shared promise when rejecting with no values", function()
134
134
  expect(Promise.rejected()).toBe(Promise.rejected())
135
135
  end)
136
+
137
+ it("rejects with nil without reporting an uncaught exception", function()
138
+ local promise = Promise.rejected(nil)
139
+
140
+ expect(promise:IsRejected()).toEqual(true)
141
+ expect(promise:_isReportableRejection(promise._rejected)).toEqual(false)
142
+
143
+ local ok, err = promise:GetResults()
144
+ expect(ok).toEqual(false)
145
+ expect(err).toEqual(nil)
146
+ end)
147
+
148
+ it("rejects with several nils without reporting an uncaught exception", function()
149
+ local promise = Promise.rejected(nil, nil, nil)
150
+
151
+ expect(promise:IsRejected()).toEqual(true)
152
+ expect(promise._rejected.n).toEqual(3)
153
+ expect(promise:_isReportableRejection(promise._rejected)).toEqual(false)
154
+ end)
136
155
  end)
137
156
 
138
157
  describe("Promise:Resolve", function()
@@ -531,6 +550,34 @@ describe("Promise:GetResults", function()
531
550
  end)
532
551
  end)
533
552
 
553
+ describe("Promise._isReportableRejection", function()
554
+ local promise = Promise.new()
555
+
556
+ it("reports a rejection that carries a value", function()
557
+ expect(promise:_isReportableRejection(table.pack("boom"))).toEqual(true)
558
+ expect(promise:_isReportableRejection(table.pack(false))).toEqual(true)
559
+ expect(promise:_isReportableRejection(table.pack(0))).toEqual(true)
560
+ expect(promise:_isReportableRejection(table.pack({ code = 500 }))).toEqual(true)
561
+ end)
562
+
563
+ it("reports a rejection whose value is not the first one", function()
564
+ expect(promise:_isReportableRejection(table.pack(nil, "boom"))).toEqual(true)
565
+ end)
566
+
567
+ it("does not report a rejection with no values", function()
568
+ expect(promise:_isReportableRejection({ n = 0 })).toEqual(false)
569
+ end)
570
+
571
+ it("does not report a rejection whose values are all nil", function()
572
+ expect(promise:_isReportableRejection(table.pack(nil))).toEqual(false)
573
+ expect(promise:_isReportableRejection(table.pack(nil, nil, nil))).toEqual(false)
574
+ end)
575
+
576
+ it("does not report a rejection carrying a single empty table", function()
577
+ expect(promise:_isReportableRejection(table.pack({}))).toEqual(false)
578
+ end)
579
+ end)
580
+
534
581
  describe("Promise._toHumanReadable", function()
535
582
  local promise = Promise.new()
536
583