@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 +20 -0
- package/package.json +6 -6
- package/src/Shared/Observable.lua +7 -3
- package/src/Shared/Rx.lua +89 -11
- package/src/Shared/Rx.spec.lua +6 -3
- package/src/Shared/Subscription.lua +20 -2
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.
|
|
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.
|
|
31
|
-
"@quenty/maid": "^2.0
|
|
32
|
-
"@quenty/promise": "^3.
|
|
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.
|
|
36
|
+
"@quenty/throttle": "^3.5.0"
|
|
37
37
|
},
|
|
38
38
|
"publishConfig": {
|
|
39
39
|
"access": "public"
|
|
40
40
|
},
|
|
41
|
-
"gitHead": "
|
|
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
|
-
|
|
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(
|
|
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.
|
|
661
|
+
outerMaid._innerSub = nil
|
|
599
662
|
|
|
600
|
-
|
|
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
|
|
package/src/Shared/Rx.spec.lua
CHANGED
|
@@ -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
|
|
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")
|