@quenty/rx 3.7.0 → 3.8.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,26 @@
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
+ # [3.8.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/rx@3.7.1...@quenty/rx@3.8.0) (2022-01-17)
7
+
8
+
9
+ ### Features
10
+
11
+ * Add Rx.toPromise(observable, cancelToken) ([1237fb4](https://github.com/Quenty/NevermoreEngine/commit/1237fb4155bb9bcca94ffd5f618fcea60026f694))
12
+ * Add stack tracing to subscription if desired, and check pending state ([6cf8bc6](https://github.com/Quenty/NevermoreEngine/commit/6cf8bc6ab5916a0a2c237a2552fd9901dca1a756))
13
+
14
+
15
+
16
+
17
+
18
+ ## [3.7.1](https://github.com/Quenty/NevermoreEngine/compare/@quenty/rx@3.7.0...@quenty/rx@3.7.1) (2022-01-16)
19
+
20
+ **Note:** Version bump only for package @quenty/rx
21
+
22
+
23
+
24
+
25
+
6
26
  # [3.7.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/rx@3.6.0...@quenty/rx@3.7.0) (2022-01-07)
7
27
 
8
28
  **Note:** Version bump only for package @quenty/rx
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quenty/rx",
3
- "version": "3.7.0",
3
+ "version": "3.8.0",
4
4
  "description": "Quenty's reactive library for Roblox",
5
5
  "keywords": [
6
6
  "Roblox",
@@ -27,16 +27,16 @@
27
27
  "Quenty"
28
28
  ],
29
29
  "dependencies": {
30
- "@quenty/loader": "^3.3.0",
31
- "@quenty/maid": "^2.0.2",
32
- "@quenty/promise": "^3.5.0",
30
+ "@quenty/loader": "^3.4.0",
31
+ "@quenty/maid": "^2.1.0",
32
+ "@quenty/promise": "^3.6.0",
33
33
  "@quenty/signal": "^2.1.0",
34
34
  "@quenty/symbol": "^2.0.1",
35
35
  "@quenty/table": "^2.1.1",
36
- "@quenty/throttle": "^3.4.0"
36
+ "@quenty/throttle": "^3.5.0"
37
37
  },
38
38
  "publishConfig": {
39
39
  "access": "public"
40
40
  },
41
- "gitHead": "5a3f3fb6c908fd3874f5ceacc70b404780275119"
41
+ "gitHead": "c094ba8f4e128cdff08919d89de226d3d65247ce"
42
42
  }
@@ -15,9 +15,13 @@
15
15
  sub:Complete() -- ends stream
16
16
  end)
17
17
 
18
- observable:Subscribe() --> Connected
19
- observable:Subscribe() --> Connected
20
- observable:Subscribe() --> Connected
18
+ local sub1 = observable:Subscribe() --> Connected
19
+ local sub2 = observable:Subscribe() --> Connected
20
+ local sub3 = observable:Subscribe() --> Connected
21
+
22
+ sub1:Destroy()
23
+ sub2:Destroy()
24
+ sub3:Destroy()
21
25
  ```
22
26
 
23
27
  Note that emitted values may be observed like this
package/src/Shared/Rx.lua CHANGED
@@ -118,6 +118,37 @@ function Rx.from(item)
118
118
  end
119
119
  end
120
120
 
121
+ --[=[
122
+ Converts a promise to an observable.
123
+ @param observable Observable<T>
124
+ @param cancelToken CancelToken?
125
+ @return Promise<T>
126
+ ]=]
127
+ function Rx.toPromise(observable, cancelToken)
128
+ local maid = Maid.new()
129
+
130
+ local promise = Promise.new(function(resolve, reject)
131
+ if cancelToken then
132
+ if cancelToken:IsCancelled() then
133
+ reject()
134
+ return
135
+ end
136
+
137
+ maid:GiveTask(cancelToken.Cancelled:Connect(function()
138
+ reject()
139
+ end))
140
+ end
141
+
142
+ maid:GiveTask(observable:Subscribe(resolve, reject, reject))
143
+ end)
144
+
145
+ promise:Finally(function()
146
+ maid:DoCleaning()
147
+ end)
148
+
149
+ return promise
150
+ end
151
+
121
152
  --[=[
122
153
  https://rxjs-dev.firebaseapp.com/api/operators/merge
123
154
 
@@ -217,6 +248,8 @@ function Rx.tap(onFire, onError, onComplete)
217
248
  assert(type(onComplete) == "function" or onComplete == nil, "Bad onComplete")
218
249
 
219
250
  return function(source)
251
+ assert(Observable.isObservable(source), "Bad observable")
252
+
220
253
  return Observable.new(function(sub)
221
254
  return source:Subscribe(
222
255
  function(...)
@@ -251,6 +284,8 @@ end
251
284
  ]=]
252
285
  function Rx.start(callback)
253
286
  return function(source)
287
+ assert(Observable.isObservable(source), "Bad observable")
288
+
254
289
  return Observable.new(function(sub)
255
290
  sub:Fire(callback())
256
291
 
@@ -268,6 +303,8 @@ end
268
303
  function Rx.startFrom(callback)
269
304
  assert(type(callback) == "function", "Bad callback")
270
305
  return function(source)
306
+ assert(Observable.isObservable(source), "Bad observable")
307
+
271
308
  return Observable.new(function(sub)
272
309
  for _, value in pairs(callback()) do
273
310
  sub:Fire(value)
@@ -289,6 +326,8 @@ function Rx.startWith(values)
289
326
  assert(type(values) == "table", "Bad values")
290
327
 
291
328
  return function(source)
329
+ assert(Observable.isObservable(source), "Bad observable")
330
+
292
331
  return Observable.new(function(sub)
293
332
  for _, item in pairs(values) do
294
333
  sub:Fire(item)
@@ -313,12 +352,14 @@ end
313
352
  ]=]
314
353
  function Rx.defaultsTo(value)
315
354
  return function(source)
355
+ assert(Observable.isObservable(source), "Bad observable")
356
+
316
357
  return Observable.new(function(sub)
317
358
  local maid = Maid.new()
318
359
 
319
360
  local fired = false
320
361
 
321
- sub:GiveTask(source:Subscribe(
362
+ maid:GiveTask(source:Subscribe(
322
363
  function(...)
323
364
  fired = true
324
365
  sub:Fire(...)
@@ -361,6 +402,8 @@ Rx.defaultsToNil = Rx.defaultsTo(nil)
361
402
  ]=]
362
403
  function Rx.endWith(values)
363
404
  return function(source)
405
+ assert(Observable.isObservable(source), "Bad observable")
406
+
364
407
  return Observable.new(function(sub)
365
408
  local maid = Maid.new()
366
409
 
@@ -405,6 +448,8 @@ function Rx.where(predicate)
405
448
  assert(type(predicate) == "function", "Bad predicate callback")
406
449
 
407
450
  return function(source)
451
+ assert(Observable.isObservable(source), "Bad observable")
452
+
408
453
  return Observable.new(function(sub)
409
454
  return source:Subscribe(
410
455
  function(...)
@@ -432,6 +477,8 @@ end
432
477
  ]=]
433
478
  function Rx.distinct()
434
479
  return function(source)
480
+ assert(Observable.isObservable(source), "Bad observable")
481
+
435
482
  return Observable.new(function(sub)
436
483
  local last = UNSET_VALUE
437
484
 
@@ -459,6 +506,8 @@ end
459
506
  function Rx.mapTo(...)
460
507
  local args = table.pack(...)
461
508
  return function(source)
509
+ assert(Observable.isObservable(source), "Bad observable")
510
+
462
511
  return Observable.new(function(sub)
463
512
  return source:Subscribe(function()
464
513
  sub:Fire(table.unpack(args, 1, args.n))
@@ -487,6 +536,8 @@ function Rx.map(project)
487
536
  assert(type(project) == "function", "Bad project callback")
488
537
 
489
538
  return function(source)
539
+ assert(Observable.isObservable(source), "Bad observable")
540
+
490
541
  return Observable.new(function(sub)
491
542
  return source:Subscribe(function(...)
492
543
  sub:Fire(project(...))
@@ -514,6 +565,8 @@ end
514
565
  ]=]
515
566
  function Rx.mergeAll()
516
567
  return function(source)
568
+ assert(Observable.isObservable(source), "Bad observable")
569
+
517
570
  return Observable.new(function(sub)
518
571
  local maid = Maid.new()
519
572
 
@@ -583,22 +636,31 @@ end
583
636
  ]=]
584
637
  function Rx.switchAll()
585
638
  return function(source)
639
+ assert(Observable.isObservable(source), "Bad observable")
640
+
586
641
  return Observable.new(function(sub)
587
642
  local outerMaid = Maid.new()
588
643
  local topComplete = false
589
644
  local insideComplete = false
590
645
  local currentInside = nil
591
646
 
592
- outerMaid:GiveTask(source:Subscribe(
647
+ outerMaid:GiveTask(function()
648
+ -- Ensure inner subscription is disconnected first. This prevents
649
+ -- the inner sub from firing while the outer is subscribed,
650
+ -- throwing a warning.
651
+ outerMaid._innerSub = nil
652
+ outerMaid._outerSuber = nil
653
+ end)
654
+
655
+ outerMaid._outerSuber = source:Subscribe(
593
656
  function(observable)
594
657
  assert(Observable.isObservable(observable), "Bad observable")
595
658
 
596
659
  insideComplete = false
597
660
  currentInside = observable
598
- outerMaid._current = nil
661
+ outerMaid._innerSub = nil
599
662
 
600
- local maid = Maid.new()
601
- maid:GiveTask(observable:Subscribe(
663
+ outerMaid._innerSub = observable:Subscribe(
602
664
  function(...)
603
665
  sub:Fire(...)
604
666
  end, -- Merge each inner observable
@@ -612,12 +674,10 @@ function Rx.switchAll()
612
674
  insideComplete = true
613
675
  if insideComplete and topComplete then
614
676
  sub:Complete()
615
- outerMaid:DoCleaning()
677
+ outerMaid:DoCleaning() -- Paranoid ensure cleanup.
616
678
  end
617
679
  end
618
- end))
619
-
620
- outerMaid._current = maid
680
+ end)
621
681
  end,
622
682
  function(...)
623
683
  sub:Fail(...) -- Also reflect failures up to the top!
@@ -627,9 +687,9 @@ function Rx.switchAll()
627
687
  topComplete = true
628
688
  if insideComplete and topComplete then
629
689
  sub:Complete()
630
- outerMaid:DoCleaning()
690
+ outerMaid:DoCleaning() -- Paranoid ensure cleanup
631
691
  end
632
- end))
692
+ end)
633
693
 
634
694
  return outerMaid
635
695
  end)
@@ -649,6 +709,8 @@ function Rx.flatMap(project, resultSelector)
649
709
  assert(type(project) == "function", "Bad project")
650
710
 
651
711
  return function(source)
712
+ assert(Observable.isObservable(source), "Bad observable")
713
+
652
714
  return Observable.new(function(sub)
653
715
  local maid = Maid.new()
654
716
 
@@ -722,6 +784,8 @@ function Rx.takeUntil(notifier)
722
784
  assert(Observable.isObservable(notifier))
723
785
 
724
786
  return function(source)
787
+ assert(Observable.isObservable(source), "Bad observable")
788
+
725
789
  return Observable.new(function(sub)
726
790
  local maid = Maid.new()
727
791
  local cancelled = false
@@ -813,6 +877,8 @@ function Rx.finalize(finalizerCallback)
813
877
  assert(type(finalizerCallback) == "function", "Bad finalizerCallback")
814
878
 
815
879
  return function(source)
880
+ assert(Observable.isObservable(source), "Bad observable")
881
+
816
882
  return Observable.new(function(sub)
817
883
  local maid = Maid.new()
818
884
 
@@ -836,6 +902,8 @@ end
836
902
  ]=]
837
903
  function Rx.combineLatestAll()
838
904
  return function(source)
905
+ assert(Observable.isObservable(source), "Bad observable")
906
+
839
907
  return Observable.new(function(sub)
840
908
  local observables = {}
841
909
  local maid = Maid.new()
@@ -895,6 +963,8 @@ function Rx.catchError(callback)
895
963
  assert(type(callback) == "function", "Bad callback")
896
964
 
897
965
  return function(source)
966
+ assert(Observable.isObservable(source), "Bad observable")
967
+
898
968
  return Observable.new(function(sub)
899
969
  local maid = Maid.new()
900
970
 
@@ -1058,6 +1128,8 @@ function Rx.take(number)
1058
1128
  assert(number >= 0, "Bad number")
1059
1129
 
1060
1130
  return function(source)
1131
+ assert(Observable.isObservable(source), "Bad observable")
1132
+
1061
1133
  return Observable.new(function(sub)
1062
1134
  if number == 0 then
1063
1135
  sub:Complete()
@@ -1135,6 +1207,8 @@ function Rx.withLatestFrom(inputObservables)
1135
1207
  end
1136
1208
 
1137
1209
  return function(source)
1210
+ assert(Observable.isObservable(source), "Bad observable")
1211
+
1138
1212
  return Observable.new(function(sub)
1139
1213
  local maid = Maid.new()
1140
1214
 
@@ -1174,6 +1248,8 @@ function Rx.scan(accumulator, seed)
1174
1248
  assert(type(accumulator) == "function", "Bad accumulator")
1175
1249
 
1176
1250
  return function(source)
1251
+ assert(Observable.isObservable(source), "Bad observable")
1252
+
1177
1253
  return Observable.new(function(sub)
1178
1254
  local current = seed
1179
1255
 
@@ -1203,6 +1279,8 @@ function Rx.throttleTime(duration, throttleConfig)
1203
1279
  assert(type(throttleConfig) == "table" or throttleConfig == nil, "Bad throttleConfig")
1204
1280
 
1205
1281
  return function(source)
1282
+ assert(Observable.isObservable(source), "Bad observable")
1283
+
1206
1284
  return Observable.new(function(sub)
1207
1285
  local maid = Maid.new()
1208
1286
 
@@ -12,11 +12,12 @@ return function()
12
12
  local externalResult
13
13
 
14
14
  it("should execute immediately", function()
15
- observe:Subscribe(function(result)
15
+ local sub = observe:Subscribe(function(result)
16
16
  externalResult = result
17
17
  end)
18
18
 
19
19
  expect(externalResult).to.be.a("table")
20
+ sub:Destroy()
20
21
  end)
21
22
  end)
22
23
 
@@ -25,12 +26,13 @@ return function()
25
26
  local externalResult
26
27
 
27
28
  it("should execute immediately", function()
28
- observe:Subscribe(function(result)
29
+ local sub = observe:Subscribe(function(result)
29
30
  externalResult = result
30
31
  end)
31
32
 
32
33
  expect(externalResult).to.be.a("table")
33
34
  expect(externalResult.value).to.equal(5)
35
+ sub:Destroy()
34
36
  end)
35
37
  end)
36
38
 
@@ -39,12 +41,13 @@ return function()
39
41
  local externalResult
40
42
 
41
43
  it("should execute immediately", function()
42
- observe:Subscribe(function(result)
44
+ local sub = observe:Subscribe(function(result)
43
45
  externalResult = result
44
46
  end)
45
47
 
46
48
  expect(externalResult).to.be.a("table")
47
49
  expect(externalResult.value).to.equal(5)
50
+ sub:Destroy()
48
51
  end)
49
52
  end)
50
53
  end
@@ -18,6 +18,8 @@ local require = require(script.Parent.loader).load(script)
18
18
 
19
19
  local MaidTaskUtils = require("MaidTaskUtils")
20
20
 
21
+ local ENABLE_STACK_TRACING = false
22
+
21
23
  local Subscription = {}
22
24
  Subscription.ClassName = "Subscription"
23
25
  Subscription.__index = Subscription
@@ -45,6 +47,7 @@ function Subscription.new(fireCallback, failCallback, completeCallback, onSubscr
45
47
 
46
48
  return setmetatable({
47
49
  _state = stateTypes.PENDING;
50
+ _source = ENABLE_STACK_TRACING and debug.traceback() or "";
48
51
  _fireCallback = fireCallback;
49
52
  _failCallback = failCallback;
50
53
  _completeCallback = completeCallback;
@@ -58,10 +61,17 @@ end
58
61
  @param ... any
59
62
  ]=]
60
63
  function Subscription:Fire(...)
61
- if self._state == stateTypes.PENDING and self._fireCallback then
62
- self._fireCallback(...)
64
+ if self._state == stateTypes.PENDING then
65
+ if self._fireCallback then
66
+ self._fireCallback(...)
67
+ end
63
68
  elseif self._state == stateTypes.CANCELLED then
64
69
  warn("[Subscription.Fire] - We are cancelled, but events are still being pushed")
70
+
71
+ if ENABLE_STACK_TRACING then
72
+ print(debug.traceback())
73
+ print(self._source)
74
+ end
65
75
  end
66
76
  end
67
77
 
@@ -153,6 +163,14 @@ function Subscription:Complete()
153
163
  self:_doCleanup()
154
164
  end
155
165
 
166
+ --[=[
167
+ Returns whether the subscription is pending.
168
+ @return boolean
169
+ ]=]
170
+ function Subscription:IsPending()
171
+ return self._state == stateTypes.PENDING
172
+ end
173
+
156
174
  function Subscription:_giveCleanup(task)
157
175
  assert(task, "Bad task")
158
176
  assert(not self._cleanupTask, "Already have _cleanupTask")