@quenty/rx 8.1.1 → 8.2.0

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,17 @@
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
+ # [8.2.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/rx@8.1.1...@quenty/rx@8.2.0) (2023-12-14)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * Rx.switchAll() and Rx.flatMap() could previously leak subscriptions if subscriptions were swapped during the emission of the signal itself ([a58da12](https://github.com/Quenty/NevermoreEngine/commit/a58da122eb4a710c932d9bd4b44231566e7f0b11))
12
+
13
+
14
+
15
+
16
+
6
17
  ## [8.1.1](https://github.com/Quenty/NevermoreEngine/compare/@quenty/rx@8.1.0...@quenty/rx@8.1.1) (2023-10-28)
7
18
 
8
19
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quenty/rx",
3
- "version": "8.1.1",
3
+ "version": "8.2.0",
4
4
  "description": "Quenty's reactive library for Roblox",
5
5
  "keywords": [
6
6
  "Roblox",
@@ -28,17 +28,17 @@
28
28
  ],
29
29
  "dependencies": {
30
30
  "@quenty/cancellabledelay": "^3.4.0",
31
- "@quenty/canceltoken": "^7.0.0",
32
- "@quenty/loader": "^7.0.0",
31
+ "@quenty/canceltoken": "^7.1.0",
32
+ "@quenty/loader": "^7.1.0",
33
33
  "@quenty/maid": "^2.6.0",
34
- "@quenty/promise": "^7.0.0",
35
- "@quenty/signal": "^3.0.0",
34
+ "@quenty/promise": "^7.1.0",
35
+ "@quenty/signal": "^3.1.0",
36
36
  "@quenty/symbol": "^2.2.0",
37
- "@quenty/table": "^3.3.0",
38
- "@quenty/throttle": "^7.0.0"
37
+ "@quenty/table": "^3.4.0",
38
+ "@quenty/throttle": "^7.1.0"
39
39
  },
40
40
  "publishConfig": {
41
41
  "access": "public"
42
42
  },
43
- "gitHead": "440aca7ce2b50b74317ee05fdc0b8d1e58001af3"
43
+ "gitHead": "2c2dbbc0cb2fbb46b4f3270c559c63890fe18b26"
44
44
  }
package/src/Shared/Rx.lua CHANGED
@@ -182,15 +182,29 @@ end
182
182
  function Rx.merge(observables)
183
183
  assert(type(observables) == "table", "Bad observables")
184
184
 
185
+ local totalCount = 0
185
186
  for _, item in pairs(observables) do
186
187
  assert(Observable.isObservable(item), "Not an observable")
188
+ totalCount = totalCount + 1
187
189
  end
188
190
 
189
191
  return Observable.new(function(sub)
190
192
  local maid = Maid.new()
193
+ local pendingCount = totalCount
191
194
 
192
195
  for _, observable in pairs(observables) do
193
- maid:GiveTask(observable:Subscribe(sub:GetFireFailComplete()))
196
+ maid:GiveTask(observable:Subscribe(function(...)
197
+ sub:Fire(...)
198
+ end, function(...)
199
+ pendingCount = pendingCount - 1
200
+ sub:Fail(...)
201
+ end, function()
202
+ -- Only complete once all are complete
203
+ pendingCount = pendingCount - 1
204
+ if pendingCount == 0 then
205
+ sub:Complete()
206
+ end
207
+ end))
194
208
  end
195
209
 
196
210
  return maid
@@ -966,9 +980,13 @@ function Rx.switchAll()
966
980
  currentInside = observable
967
981
  outerMaid._innerSub = nil
968
982
 
969
- outerMaid._innerSub = observable:Subscribe(
983
+ local subscription = observable:Subscribe(
970
984
  function(...)
971
- sub:Fire(...)
985
+ if currentInside == observable then
986
+ sub:Fire(...)
987
+ else
988
+ warn(string.format("[Rx.switchAll] - Observable is still firing despite disconnect (%q)", observable._source))
989
+ end
972
990
  end, -- Merge each inner observable
973
991
  function(...)
974
992
  if currentInside == observable then
@@ -984,6 +1002,13 @@ function Rx.switchAll()
984
1002
  end
985
1003
  end
986
1004
  end)
1005
+
1006
+ if currentInside == observable then
1007
+ outerMaid._innerSub = subscription
1008
+ else
1009
+ -- We cleaned up while connecting
1010
+ subscription:Destroy()
1011
+ end
987
1012
  end,
988
1013
  function(...)
989
1014
  sub:Fail(...) -- Also reflect failures up to the top!
@@ -1034,7 +1059,7 @@ function Rx.flatMap(project, resultSelector)
1034
1059
 
1035
1060
  local innerMaid = Maid.new()
1036
1061
 
1037
- innerMaid:GiveTask(observable:Subscribe(
1062
+ local subscription = innerMaid:Add(observable:Subscribe(
1038
1063
  function(...)
1039
1064
  -- Merge each inner observable
1040
1065
  if resultSelector then
@@ -1055,12 +1080,16 @@ function Rx.flatMap(project, resultSelector)
1055
1080
  end
1056
1081
  end))
1057
1082
 
1058
- local key = maid:GiveTask(innerMaid)
1083
+ if subscription:IsPending() then
1084
+ local key = maid:GiveTask(innerMaid)
1059
1085
 
1060
- -- Cleanup
1061
- innerMaid:GiveTask(function()
1062
- maid[key] = nil
1063
- end)
1086
+ -- Cleanup
1087
+ innerMaid:GiveTask(function()
1088
+ maid[key] = nil
1089
+ end)
1090
+ else
1091
+ subscription:Destroy()
1092
+ end
1064
1093
  end,
1065
1094
  function(...)
1066
1095
  sub:Fail(...) -- Also reflect failures up to the top!
@@ -1794,6 +1823,12 @@ end
1794
1823
 
1795
1824
  --[=[
1796
1825
  Throttles emission of observables on the defer stack to the last emission.
1826
+
1827
+ :::tip
1828
+ There's a limited re-entrance amount for this. However, this can prevent computation being done repeatedly if
1829
+ stuff is being added all at once. Use with care.
1830
+ :::
1831
+
1797
1832
  @return (source: Observable) -> Observable
1798
1833
  ]=]
1799
1834
  function Rx.throttleDefer()