@quenty/rx 13.17.0 → 13.17.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 +11 -0
- package/package.json +13 -12
- package/src/Shared/Observable.lua +30 -11
- package/src/Shared/ObservableSubscriptionTable.lua +29 -12
- package/src/Shared/Rx.lua +134 -115
- package/src/Shared/Subscription.lua +71 -27
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
|
+
## [13.17.1](https://github.com/Quenty/NevermoreEngine/compare/@quenty/rx@13.17.0...@quenty/rx@13.17.1) (2025-04-05)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* Add types to packages ([2374fb2](https://github.com/Quenty/NevermoreEngine/commit/2374fb2b043cfbe0e9b507b3316eec46a4e353a0))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
6
17
|
# [13.17.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/rx@13.16.2...@quenty/rx@13.17.0) (2025-04-02)
|
|
7
18
|
|
|
8
19
|
**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": "13.17.
|
|
3
|
+
"version": "13.17.1",
|
|
4
4
|
"description": "Quenty's reactive library for Roblox",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Roblox",
|
|
@@ -27,19 +27,20 @@
|
|
|
27
27
|
"Quenty"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@quenty/cancellabledelay": "^3.5.
|
|
31
|
-
"@quenty/canceltoken": "^11.11.
|
|
32
|
-
"@quenty/ducktype": "^5.8.
|
|
33
|
-
"@quenty/loader": "^10.8.
|
|
34
|
-
"@quenty/maid": "^3.4.
|
|
35
|
-
"@quenty/promise": "^10.10.
|
|
36
|
-
"@quenty/signal": "^7.10.
|
|
37
|
-
"@quenty/symbol": "^3.4.
|
|
38
|
-
"@quenty/table": "^3.7.
|
|
39
|
-
"@quenty/throttle": "^10.9.
|
|
30
|
+
"@quenty/cancellabledelay": "^3.5.1",
|
|
31
|
+
"@quenty/canceltoken": "^11.11.2",
|
|
32
|
+
"@quenty/ducktype": "^5.8.2",
|
|
33
|
+
"@quenty/loader": "^10.8.1",
|
|
34
|
+
"@quenty/maid": "^3.4.1",
|
|
35
|
+
"@quenty/promise": "^10.10.2",
|
|
36
|
+
"@quenty/signal": "^7.10.1",
|
|
37
|
+
"@quenty/symbol": "^3.4.1",
|
|
38
|
+
"@quenty/table": "^3.7.2",
|
|
39
|
+
"@quenty/throttle": "^10.9.1",
|
|
40
|
+
"@quenty/typeutils": "^1.0.1"
|
|
40
41
|
},
|
|
41
42
|
"publishConfig": {
|
|
42
43
|
"access": "public"
|
|
43
44
|
},
|
|
44
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "78c3ac0ab08dd18085b6e6e6e4f745e76ed99f68"
|
|
45
46
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
--!strict
|
|
1
2
|
--[=[
|
|
2
3
|
Observables are like an [signal](/api/Signal), except they do not execute code
|
|
3
4
|
until the observable is subscribed to. This follows the standard
|
|
@@ -59,6 +60,7 @@ local require = require(script.Parent.loader).load(script)
|
|
|
59
60
|
|
|
60
61
|
local Subscription = require("Subscription")
|
|
61
62
|
local DuckTypeUtils = require("DuckTypeUtils")
|
|
63
|
+
local _MaidTaskUtils = require("MaidTaskUtils")
|
|
62
64
|
|
|
63
65
|
local ENABLE_STACK_TRACING = false
|
|
64
66
|
|
|
@@ -66,12 +68,24 @@ local Observable = {}
|
|
|
66
68
|
Observable.ClassName = "Observable"
|
|
67
69
|
Observable.__index = Observable
|
|
68
70
|
|
|
71
|
+
export type OnSubscribe<T...> = (subscription: Subscription.Subscription<T...>) -> _MaidTaskUtils.MaidTask?
|
|
72
|
+
|
|
73
|
+
export type Transformer<T..., U...> = (observable: Observable<T...>) -> Observable<U...>
|
|
74
|
+
|
|
75
|
+
export type Observable<T...> = typeof(setmetatable(
|
|
76
|
+
{} :: {
|
|
77
|
+
_source: string?,
|
|
78
|
+
_onSubscribe: OnSubscribe<T...>,
|
|
79
|
+
},
|
|
80
|
+
Observable
|
|
81
|
+
))
|
|
82
|
+
|
|
69
83
|
--[=[
|
|
70
84
|
Returns whether or not a value is an observable.
|
|
71
85
|
@param item any
|
|
72
86
|
@return boolean
|
|
73
87
|
]=]
|
|
74
|
-
function Observable.isObservable(item)
|
|
88
|
+
function Observable.isObservable(item: any): boolean
|
|
75
89
|
return DuckTypeUtils.isImplementation(Observable, item)
|
|
76
90
|
end
|
|
77
91
|
|
|
@@ -83,7 +97,7 @@ end
|
|
|
83
97
|
return Observable.new(function(sub)
|
|
84
98
|
local maid = Maid.new()
|
|
85
99
|
|
|
86
|
-
for _, item in
|
|
100
|
+
for _, item in parent:GetChildren() do
|
|
87
101
|
sub:Fire(item)
|
|
88
102
|
end
|
|
89
103
|
maid:GiveTask(parent.ChildAdded:Connect(function(child)
|
|
@@ -103,12 +117,12 @@ end
|
|
|
103
117
|
@param onSubscribe (subscription: Subscription<T>) -> MaidTask
|
|
104
118
|
@return Observable<T>
|
|
105
119
|
]=]
|
|
106
|
-
function Observable.new(onSubscribe)
|
|
120
|
+
function Observable.new<T...>(onSubscribe: OnSubscribe<T...>): Observable<T...>
|
|
107
121
|
assert(type(onSubscribe) == "function", "Bad onSubscribe")
|
|
108
122
|
|
|
109
123
|
return setmetatable({
|
|
110
|
-
_source = if ENABLE_STACK_TRACING then debug.traceback("Observable.new()", 2) else nil
|
|
111
|
-
_onSubscribe = onSubscribe
|
|
124
|
+
_source = if ENABLE_STACK_TRACING then debug.traceback("Observable.new()", 2) else nil,
|
|
125
|
+
_onSubscribe = onSubscribe,
|
|
112
126
|
}, Observable)
|
|
113
127
|
end
|
|
114
128
|
|
|
@@ -132,11 +146,11 @@ end
|
|
|
132
146
|
@param transformers { (observable: Observable<T>) -> Observable<T> }
|
|
133
147
|
@return Observable<T>
|
|
134
148
|
]=]
|
|
135
|
-
function Observable
|
|
149
|
+
function Observable.Pipe<T...>(self: Observable<T...>, transformers: { Transformer<T..., ...any> }): Observable<...any>
|
|
136
150
|
assert(type(transformers) == "table", "Bad transformers")
|
|
137
151
|
|
|
138
|
-
local current = self
|
|
139
|
-
for _, transformer in
|
|
152
|
+
local current: any = self
|
|
153
|
+
for _, transformer in transformers do
|
|
140
154
|
assert(type(transformer) == "function", "Bad transformer")
|
|
141
155
|
current = transformer(current)
|
|
142
156
|
assert(Observable.isObservable(current), "Transformer must return an observable")
|
|
@@ -154,12 +168,17 @@ end
|
|
|
154
168
|
@param completeCallback function?
|
|
155
169
|
@return MaidTask
|
|
156
170
|
]=]
|
|
157
|
-
function Observable
|
|
171
|
+
function Observable.Subscribe<T...>(
|
|
172
|
+
self: Observable<T...>,
|
|
173
|
+
fireCallback: Subscription.FireCallback<T...>?,
|
|
174
|
+
failCallback: Subscription.FailCallback?,
|
|
175
|
+
completeCallback: Subscription.CompleteCallback?
|
|
176
|
+
): Subscription.Subscription<T...>
|
|
158
177
|
local sub = Subscription.new(fireCallback, failCallback, completeCallback, self._source)
|
|
159
178
|
|
|
160
179
|
sub:_assignCleanup(self._onSubscribe(sub))
|
|
161
180
|
|
|
162
|
-
return sub
|
|
181
|
+
return sub :: any
|
|
163
182
|
end
|
|
164
183
|
|
|
165
|
-
return Observable
|
|
184
|
+
return Observable
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
--!strict
|
|
1
2
|
--[=[
|
|
2
3
|
This allows the storage of subscriptions for keys, such that something
|
|
3
4
|
can subscribe onto a key, and events can be invoked onto keys.
|
|
@@ -7,13 +8,21 @@
|
|
|
7
8
|
local require = require(script.Parent.loader).load(script)
|
|
8
9
|
|
|
9
10
|
local Observable = require("Observable")
|
|
11
|
+
local _Subscription = require("Subscription")
|
|
10
12
|
|
|
11
13
|
local ObservableSubscriptionTable = {}
|
|
12
14
|
ObservableSubscriptionTable.ClassName = "ObservableSubscriptionTable"
|
|
13
15
|
ObservableSubscriptionTable.__index = ObservableSubscriptionTable
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
export type ObservableSubscriptionTable<T...> = typeof(setmetatable(
|
|
18
|
+
{} :: {
|
|
19
|
+
_subMap: { [any]: { _Subscription.Subscription<T...> } },
|
|
20
|
+
},
|
|
21
|
+
ObservableSubscriptionTable
|
|
22
|
+
))
|
|
23
|
+
|
|
24
|
+
function ObservableSubscriptionTable.new<T...>(): ObservableSubscriptionTable<T...>
|
|
25
|
+
local self = setmetatable({} :: any, ObservableSubscriptionTable)
|
|
17
26
|
|
|
18
27
|
self._subMap = {} -- { TKey: Subscription<TEmit> }
|
|
19
28
|
|
|
@@ -25,7 +34,7 @@ end
|
|
|
25
34
|
@param key TKey
|
|
26
35
|
@param ... TEmit
|
|
27
36
|
]=]
|
|
28
|
-
function ObservableSubscriptionTable
|
|
37
|
+
function ObservableSubscriptionTable.Fire<T...>(self: ObservableSubscriptionTable<T...>, key: any, ...)
|
|
29
38
|
assert(key ~= nil, "Bad key")
|
|
30
39
|
|
|
31
40
|
local subs = self._subMap[key]
|
|
@@ -34,7 +43,7 @@ function ObservableSubscriptionTable:Fire(key, ...)
|
|
|
34
43
|
end
|
|
35
44
|
|
|
36
45
|
-- Make a copy so we don't have to worry about our last changing
|
|
37
|
-
for _, sub in
|
|
46
|
+
for _, sub: any in table.clone(subs) do
|
|
38
47
|
if sub:IsPending() then
|
|
39
48
|
-- TODO: Use connection here
|
|
40
49
|
task.spawn(sub.Fire, sub, ...)
|
|
@@ -48,7 +57,7 @@ end
|
|
|
48
57
|
@param key TKey
|
|
49
58
|
@return boolean
|
|
50
59
|
]=]
|
|
51
|
-
function ObservableSubscriptionTable
|
|
60
|
+
function ObservableSubscriptionTable.HasSubscriptions<T...>(self: ObservableSubscriptionTable<T...>, key: any): boolean
|
|
52
61
|
return self._subMap[key] ~= nil
|
|
53
62
|
end
|
|
54
63
|
|
|
@@ -57,7 +66,7 @@ end
|
|
|
57
66
|
|
|
58
67
|
@param key TKey
|
|
59
68
|
]=]
|
|
60
|
-
function ObservableSubscriptionTable
|
|
69
|
+
function ObservableSubscriptionTable.Complete<T...>(self: ObservableSubscriptionTable<T...>, key: any)
|
|
61
70
|
local subs = self._subMap[key]
|
|
62
71
|
if not subs then
|
|
63
72
|
return
|
|
@@ -66,7 +75,7 @@ function ObservableSubscriptionTable:Complete(key)
|
|
|
66
75
|
local subsToComplete = table.clone(subs)
|
|
67
76
|
self._subMap[key] = nil
|
|
68
77
|
|
|
69
|
-
for _, sub in
|
|
78
|
+
for _, sub: any in subsToComplete do
|
|
70
79
|
if sub:IsPending() then
|
|
71
80
|
task.spawn(sub.Complete, sub)
|
|
72
81
|
end
|
|
@@ -78,7 +87,7 @@ end
|
|
|
78
87
|
|
|
79
88
|
@param key TKey
|
|
80
89
|
]=]
|
|
81
|
-
function ObservableSubscriptionTable
|
|
90
|
+
function ObservableSubscriptionTable.Fail<T...>(self: ObservableSubscriptionTable<T...>, key: any)
|
|
82
91
|
local subs = self._subMap[key]
|
|
83
92
|
if not subs then
|
|
84
93
|
return
|
|
@@ -87,20 +96,26 @@ function ObservableSubscriptionTable:Fail(key)
|
|
|
87
96
|
local subsToFail = table.clone(subs)
|
|
88
97
|
self._subMap[key] = nil
|
|
89
98
|
|
|
90
|
-
for _, sub in
|
|
99
|
+
for _, sub: any in subsToFail do
|
|
91
100
|
if sub:IsPending() then
|
|
92
101
|
task.spawn(sub.Fail, sub)
|
|
93
102
|
end
|
|
94
103
|
end
|
|
95
104
|
end
|
|
96
105
|
|
|
106
|
+
export type RetrieveInitialValue<T...> = (sub: _Subscription.Subscription<T...>) -> ()
|
|
107
|
+
|
|
97
108
|
--[=[
|
|
98
109
|
Observes for the key
|
|
99
110
|
@param key TKey
|
|
100
111
|
@param retrieveInitialValue callback -- Optional
|
|
101
112
|
@return Observable<TEmit>
|
|
102
113
|
]=]
|
|
103
|
-
function ObservableSubscriptionTable
|
|
114
|
+
function ObservableSubscriptionTable.Observe<T...>(
|
|
115
|
+
self: ObservableSubscriptionTable<T...>,
|
|
116
|
+
key: any,
|
|
117
|
+
retrieveInitialValue: RetrieveInitialValue<T...>?
|
|
118
|
+
): Observable.Observable<T...>
|
|
104
119
|
assert(key ~= nil, "Bad key")
|
|
105
120
|
|
|
106
121
|
return Observable.new(function(sub)
|
|
@@ -142,12 +157,14 @@ end
|
|
|
142
157
|
--[=[
|
|
143
158
|
Completes all subscriptions and removes them from the list.
|
|
144
159
|
]=]
|
|
145
|
-
function ObservableSubscriptionTable
|
|
160
|
+
function ObservableSubscriptionTable.Destroy<T...>(self: ObservableSubscriptionTable<T...>)
|
|
146
161
|
while next(self._subMap) do
|
|
147
162
|
local key, list = next(self._subMap)
|
|
163
|
+
assert(key, "Key should not be nil")
|
|
164
|
+
|
|
148
165
|
self._subMap[key] = nil
|
|
149
166
|
|
|
150
|
-
for _, sub in
|
|
167
|
+
for _, sub: any in list do
|
|
151
168
|
if sub:IsPending() then
|
|
152
169
|
task.spawn(sub.Complete, sub)
|
|
153
170
|
end
|
package/src/Shared/Rx.lua
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
--!strict
|
|
1
2
|
--[=[
|
|
2
3
|
Observable rx library for Roblox by Quenty. This provides a variety of
|
|
3
4
|
composition classes to be used, and is the primary entry point for an
|
|
@@ -12,14 +13,20 @@
|
|
|
12
13
|
|
|
13
14
|
local require = require(script.Parent.loader).load(script)
|
|
14
15
|
|
|
16
|
+
local CancelToken = require("CancelToken")
|
|
15
17
|
local Maid = require("Maid")
|
|
18
|
+
local MaidTaskUtils = require("MaidTaskUtils")
|
|
16
19
|
local Observable = require("Observable")
|
|
17
20
|
local Promise = require("Promise")
|
|
18
21
|
local Symbol = require("Symbol")
|
|
19
22
|
local ThrottledFunction = require("ThrottledFunction")
|
|
23
|
+
local _Subscription = require("Subscription")
|
|
20
24
|
local cancellableDelay = require("cancellableDelay")
|
|
21
|
-
local
|
|
22
|
-
|
|
25
|
+
local _Signal = require("Signal")
|
|
26
|
+
|
|
27
|
+
export type Map<Key, Value> = { [Key]: Value }
|
|
28
|
+
export type Set<T> = { [T]: true }
|
|
29
|
+
export type Predicate<T...> = (T...) -> boolean
|
|
23
30
|
|
|
24
31
|
local UNSET_VALUE = Symbol.named("unsetValue")
|
|
25
32
|
local function identity(...)
|
|
@@ -42,8 +49,11 @@ end
|
|
|
42
49
|
local Rx = {
|
|
43
50
|
EMPTY = Observable.new(function(sub)
|
|
44
51
|
sub:Complete()
|
|
52
|
+
return
|
|
53
|
+
end),
|
|
54
|
+
NEVER = Observable.new(function(_)
|
|
55
|
+
return
|
|
45
56
|
end),
|
|
46
|
-
NEVER = Observable.new(function(_) end),
|
|
47
57
|
}
|
|
48
58
|
|
|
49
59
|
--[=[
|
|
@@ -55,7 +65,7 @@ local Rx = {
|
|
|
55
65
|
]=]
|
|
56
66
|
function Rx.pipe(transformers)
|
|
57
67
|
assert(type(transformers) == "table", "Bad transformers")
|
|
58
|
-
for index, transformer in
|
|
68
|
+
for index, transformer in transformers do
|
|
59
69
|
if type(transformer) ~= "function" then
|
|
60
70
|
error(
|
|
61
71
|
string.format(
|
|
@@ -71,7 +81,7 @@ function Rx.pipe(transformers)
|
|
|
71
81
|
assert(source, "Bad source")
|
|
72
82
|
|
|
73
83
|
local current = source
|
|
74
|
-
for key, transformer in
|
|
84
|
+
for key, transformer in transformers do
|
|
75
85
|
current = transformer(current)
|
|
76
86
|
|
|
77
87
|
if not (type(current) == "table" and current.ClassName == "Observable") then
|
|
@@ -102,7 +112,7 @@ end
|
|
|
102
112
|
@param ... any -- Arguments to emit
|
|
103
113
|
@return Observable
|
|
104
114
|
]=]
|
|
105
|
-
function Rx.of(...)
|
|
115
|
+
function Rx.of<T...>(...: T...): Observable.Observable<T...>
|
|
106
116
|
local args = table.pack(...)
|
|
107
117
|
|
|
108
118
|
return Observable.new(function(sub)
|
|
@@ -111,6 +121,7 @@ function Rx.of(...)
|
|
|
111
121
|
end
|
|
112
122
|
|
|
113
123
|
sub:Complete()
|
|
124
|
+
return
|
|
114
125
|
end)
|
|
115
126
|
end
|
|
116
127
|
|
|
@@ -125,6 +136,7 @@ function Rx.failed(...)
|
|
|
125
136
|
|
|
126
137
|
return Observable.new(function(sub)
|
|
127
138
|
sub:Fail(table.unpack(args, 1, args.n))
|
|
139
|
+
return
|
|
128
140
|
end)
|
|
129
141
|
end
|
|
130
142
|
|
|
@@ -194,11 +206,11 @@ end
|
|
|
194
206
|
@param observables { Observable }
|
|
195
207
|
@return Observable
|
|
196
208
|
]=]
|
|
197
|
-
function Rx.merge(observables)
|
|
209
|
+
function Rx.merge<T...>(observables: { Observable.Observable<T...> }): Observable.Observable<T...>
|
|
198
210
|
assert(type(observables) == "table", "Bad observables")
|
|
199
211
|
|
|
200
212
|
local totalCount = 0
|
|
201
|
-
for _, item in
|
|
213
|
+
for _, item in observables do
|
|
202
214
|
assert(Observable.isObservable(item), "Not an observable")
|
|
203
215
|
totalCount = totalCount + 1
|
|
204
216
|
end
|
|
@@ -207,7 +219,7 @@ function Rx.merge(observables)
|
|
|
207
219
|
local maid = Maid.new()
|
|
208
220
|
local pendingCount = totalCount
|
|
209
221
|
|
|
210
|
-
for _, observable in
|
|
222
|
+
for _, observable: any in observables do
|
|
211
223
|
maid:GiveTask(observable:Subscribe(function(...)
|
|
212
224
|
sub:Fire(...)
|
|
213
225
|
end, function(...)
|
|
@@ -233,10 +245,10 @@ end
|
|
|
233
245
|
@param event Signal<T>
|
|
234
246
|
@return Observable<T>
|
|
235
247
|
]=]
|
|
236
|
-
function Rx.fromSignal(event)
|
|
248
|
+
function Rx.fromSignal<T...>(event: _Signal.Signal<T...> | RBXScriptSignal): Observable.Observable<T...>
|
|
237
249
|
return Observable.new(function(sub)
|
|
238
250
|
-- This stream never completes or fails!
|
|
239
|
-
return event:Connect(function(...)
|
|
251
|
+
return (event :: any):Connect(function(...)
|
|
240
252
|
sub:Fire(...)
|
|
241
253
|
end)
|
|
242
254
|
end)
|
|
@@ -249,7 +261,7 @@ end
|
|
|
249
261
|
@param promise Promise<T>
|
|
250
262
|
@return Observable<T>
|
|
251
263
|
]=]
|
|
252
|
-
function Rx.fromPromise(promise)
|
|
264
|
+
function Rx.fromPromise<T...>(promise: Promise.Promise<T...>): Observable.Observable<T...>
|
|
253
265
|
assert(Promise.isPromise(promise), "Bad promise")
|
|
254
266
|
|
|
255
267
|
return Observable.new(function(sub)
|
|
@@ -328,7 +340,7 @@ end
|
|
|
328
340
|
@param callback function
|
|
329
341
|
@return (source: Observable) -> Observable
|
|
330
342
|
]=]
|
|
331
|
-
function Rx.start(callback)
|
|
343
|
+
function Rx.start<T...>(callback: () -> T...): Observable.Transformer<T..., T...>
|
|
332
344
|
return function(source)
|
|
333
345
|
assert(Observable.isObservable(source), "Bad observable")
|
|
334
346
|
|
|
@@ -348,10 +360,10 @@ end
|
|
|
348
360
|
|
|
349
361
|
@return (source: Observable) -> Observable
|
|
350
362
|
]=]
|
|
351
|
-
function Rx.share()
|
|
352
|
-
return function(source)
|
|
363
|
+
function Rx.share<T...>(): Observable.Transformer<T..., T...>
|
|
364
|
+
return function(source): Observable.Observable<T...>
|
|
353
365
|
local shareMaid = Maid.new()
|
|
354
|
-
local subs = {}
|
|
366
|
+
local subs: { _Subscription.Subscription<T...> } = {}
|
|
355
367
|
|
|
356
368
|
local lastFail = UNSET_VALUE
|
|
357
369
|
local lastComplete = UNSET_VALUE
|
|
@@ -362,17 +374,17 @@ function Rx.share()
|
|
|
362
374
|
lastComplete = UNSET_VALUE
|
|
363
375
|
|
|
364
376
|
shareMaid._currentSub = source:Subscribe(function(...)
|
|
365
|
-
for _, sub in
|
|
377
|
+
for _, sub: any in subs do
|
|
366
378
|
sub:Fire(...)
|
|
367
379
|
end
|
|
368
380
|
end, function(...)
|
|
369
381
|
lastFail = table.pack(...)
|
|
370
|
-
for _, sub in
|
|
382
|
+
for _, sub: any in subs do
|
|
371
383
|
sub:Fail(...)
|
|
372
384
|
end
|
|
373
385
|
end, function(...)
|
|
374
386
|
lastComplete = table.pack(...)
|
|
375
|
-
for _, sub in
|
|
387
|
+
for _, sub: any in subs do
|
|
376
388
|
sub:Complete(...)
|
|
377
389
|
end
|
|
378
390
|
end)
|
|
@@ -412,7 +424,7 @@ function Rx.share()
|
|
|
412
424
|
end
|
|
413
425
|
end
|
|
414
426
|
end
|
|
415
|
-
end)
|
|
427
|
+
end) :: any
|
|
416
428
|
end
|
|
417
429
|
end
|
|
418
430
|
|
|
@@ -423,27 +435,32 @@ end
|
|
|
423
435
|
@param windowTimeSeconds number? -- Time
|
|
424
436
|
@return (source: Observable) -> Observable
|
|
425
437
|
]=]
|
|
426
|
-
function Rx.shareReplay(bufferSize: number?, windowTimeSeconds: number?)
|
|
438
|
+
function Rx.shareReplay<T...>(bufferSize: number?, windowTimeSeconds: number?): Observable.Transformer<T..., T...>
|
|
427
439
|
assert(type(bufferSize) == "number" or bufferSize == nil, "Bad bufferSize")
|
|
428
440
|
assert(type(windowTimeSeconds) == "number" or windowTimeSeconds == nil, "Bad windowTimeSeconds")
|
|
429
441
|
|
|
430
|
-
|
|
431
|
-
|
|
442
|
+
local maxBufferSize = bufferSize or math.huge
|
|
443
|
+
local windowTime = windowTimeSeconds or math.huge
|
|
432
444
|
|
|
433
445
|
return function(source)
|
|
434
446
|
local shareMaid = Maid.new()
|
|
435
|
-
local subs = {}
|
|
447
|
+
local subs: { _Subscription.Subscription<T...> } = {}
|
|
436
448
|
|
|
437
|
-
|
|
449
|
+
type Event = {
|
|
450
|
+
n: number,
|
|
451
|
+
timestamp: number,
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
local buffer: { Event } = {}
|
|
438
455
|
local lastFail = UNSET_VALUE
|
|
439
456
|
local lastComplete = UNSET_VALUE
|
|
440
457
|
|
|
441
|
-
local function getEventsCopy()
|
|
458
|
+
local function getEventsCopy(): { Event }
|
|
442
459
|
local now = os.clock()
|
|
443
460
|
local events = {}
|
|
444
461
|
|
|
445
|
-
for _, event in
|
|
446
|
-
if (now - event.timestamp) <=
|
|
462
|
+
for _, event in buffer do
|
|
463
|
+
if (now - event.timestamp) <= windowTime then
|
|
447
464
|
table.insert(events, event)
|
|
448
465
|
end
|
|
449
466
|
end
|
|
@@ -460,17 +477,17 @@ function Rx.shareReplay(bufferSize: number?, windowTimeSeconds: number?)
|
|
|
460
477
|
shareMaid._currentSub = source:Subscribe(function(...)
|
|
461
478
|
-- TODO: also prune events by timestamp
|
|
462
479
|
|
|
463
|
-
if #buffer + 1 >
|
|
480
|
+
if #buffer + 1 > maxBufferSize then
|
|
464
481
|
table.remove(buffer, 1) -- O(n), not great.
|
|
465
482
|
end
|
|
466
483
|
|
|
467
484
|
-- Queue before we start
|
|
468
|
-
local event = table.pack(...)
|
|
485
|
+
local event: any = table.pack(...)
|
|
469
486
|
event.timestamp = os.clock()
|
|
470
487
|
table.insert(buffer, event)
|
|
471
488
|
|
|
472
489
|
-- Copy subs so removal doesn't affect replay
|
|
473
|
-
for _, sub in
|
|
490
|
+
for _, sub: any in table.clone(subs) do
|
|
474
491
|
if sub:IsPending() then
|
|
475
492
|
sub:Fire(table.unpack(event, 1, event.n))
|
|
476
493
|
end
|
|
@@ -479,7 +496,7 @@ function Rx.shareReplay(bufferSize: number?, windowTimeSeconds: number?)
|
|
|
479
496
|
lastFail = table.pack(...)
|
|
480
497
|
|
|
481
498
|
-- Copy subs so removal doesn't affect replay
|
|
482
|
-
for _, sub in
|
|
499
|
+
for _, sub: any in table.clone(subs) do
|
|
483
500
|
if sub:IsPending() then
|
|
484
501
|
sub:Fail(...)
|
|
485
502
|
end
|
|
@@ -488,7 +505,7 @@ function Rx.shareReplay(bufferSize: number?, windowTimeSeconds: number?)
|
|
|
488
505
|
lastComplete = table.pack(...)
|
|
489
506
|
|
|
490
507
|
-- Copy subs so removal doesn't affect replay
|
|
491
|
-
for _, sub in
|
|
508
|
+
for _, sub: any in table.clone(subs) do
|
|
492
509
|
if sub:IsPending() then
|
|
493
510
|
sub:Complete(...)
|
|
494
511
|
end
|
|
@@ -521,7 +538,7 @@ function Rx.shareReplay(bufferSize: number?, windowTimeSeconds: number?)
|
|
|
521
538
|
table.insert(subs, sub)
|
|
522
539
|
|
|
523
540
|
-- Firing could lead to re-entrance. Lets just use the buffer as-is.
|
|
524
|
-
for _, item in
|
|
541
|
+
for _, item in getEventsCopy() do
|
|
525
542
|
sub:Fire(table.unpack(item, 1, item.n))
|
|
526
543
|
end
|
|
527
544
|
|
|
@@ -537,7 +554,7 @@ function Rx.shareReplay(bufferSize: number?, windowTimeSeconds: number?)
|
|
|
537
554
|
end
|
|
538
555
|
end
|
|
539
556
|
end
|
|
540
|
-
end)
|
|
557
|
+
end) :: Observable.Observable<T...>
|
|
541
558
|
end
|
|
542
559
|
end
|
|
543
560
|
|
|
@@ -546,7 +563,7 @@ end
|
|
|
546
563
|
|
|
547
564
|
@return (source: Observable) -> Observable
|
|
548
565
|
]=]
|
|
549
|
-
function Rx.cache()
|
|
566
|
+
function Rx.cache<T...>(): Observable.Transformer<T..., T...>
|
|
550
567
|
return Rx.shareReplay(1)
|
|
551
568
|
end
|
|
552
569
|
|
|
@@ -556,19 +573,19 @@ end
|
|
|
556
573
|
@param callback () -> { T }
|
|
557
574
|
@return (source: Observable) -> Observable
|
|
558
575
|
]=]
|
|
559
|
-
function Rx.startFrom(callback)
|
|
576
|
+
function Rx.startFrom<T, U>(callback: () -> { U }): Observable.Transformer<(T), (U | T)>
|
|
560
577
|
assert(type(callback) == "function", "Bad callback")
|
|
561
578
|
|
|
562
579
|
return function(source)
|
|
563
580
|
assert(Observable.isObservable(source), "Bad observable")
|
|
564
581
|
|
|
565
582
|
return Observable.new(function(sub)
|
|
566
|
-
for _, value in
|
|
583
|
+
for _, value in callback() do
|
|
567
584
|
sub:Fire(value)
|
|
568
585
|
end
|
|
569
586
|
|
|
570
587
|
return source:Subscribe(sub:GetFireFailComplete())
|
|
571
|
-
end)
|
|
588
|
+
end) :: any
|
|
572
589
|
end
|
|
573
590
|
end
|
|
574
591
|
|
|
@@ -579,19 +596,19 @@ end
|
|
|
579
596
|
@param values { T }
|
|
580
597
|
@return (source: Observable) -> Observable
|
|
581
598
|
]=]
|
|
582
|
-
function Rx.startWith(values)
|
|
599
|
+
function Rx.startWith<T, U>(values: { U }): Observable.Transformer<(T), (T | U)>
|
|
583
600
|
assert(type(values) == "table", "Bad values")
|
|
584
601
|
|
|
585
602
|
return function(source)
|
|
586
603
|
assert(Observable.isObservable(source), "Bad observable")
|
|
587
604
|
|
|
588
605
|
return Observable.new(function(sub)
|
|
589
|
-
for _, item in
|
|
606
|
+
for _, item in values do
|
|
590
607
|
sub:Fire(item)
|
|
591
608
|
end
|
|
592
609
|
|
|
593
610
|
return source:Subscribe(sub:GetFireFailComplete())
|
|
594
|
-
end)
|
|
611
|
+
end) :: any
|
|
595
612
|
end
|
|
596
613
|
end
|
|
597
614
|
|
|
@@ -604,24 +621,24 @@ end
|
|
|
604
621
|
|
|
605
622
|
https://reactivex.io/documentation/operators/scan.html
|
|
606
623
|
|
|
607
|
-
@param accumulator
|
|
608
|
-
@param seed
|
|
624
|
+
@param accumulator (T, U...) -> T
|
|
625
|
+
@param seed T?
|
|
609
626
|
@return (source: Observable) -> Observable
|
|
610
627
|
]=]
|
|
611
|
-
function Rx.scan(accumulator, seed)
|
|
628
|
+
function Rx.scan<T, U...>(accumulator: (T?, U...) -> T, seed: T?): Observable.Transformer<(U...), (T)>
|
|
612
629
|
assert(type(accumulator) == "function", "Bad accumulator")
|
|
613
630
|
|
|
614
631
|
return function(source)
|
|
615
632
|
assert(Observable.isObservable(source), "Bad observable")
|
|
616
633
|
|
|
617
634
|
return Observable.new(function(sub)
|
|
618
|
-
local current = seed
|
|
635
|
+
local current: T? = seed
|
|
619
636
|
|
|
620
637
|
return source:Subscribe(function(...)
|
|
621
638
|
current = accumulator(current, ...)
|
|
622
639
|
sub:Fire(current)
|
|
623
640
|
end, sub:GetFailComplete())
|
|
624
|
-
end)
|
|
641
|
+
end) :: any
|
|
625
642
|
end
|
|
626
643
|
end
|
|
627
644
|
|
|
@@ -747,12 +764,12 @@ function Rx.endWith(values)
|
|
|
747
764
|
maid:GiveTask(source:Subscribe(function(...)
|
|
748
765
|
sub:Fire(...)
|
|
749
766
|
end, function(...)
|
|
750
|
-
for _, item in
|
|
767
|
+
for _, item in values do
|
|
751
768
|
sub:Fire(item)
|
|
752
769
|
end
|
|
753
770
|
sub:Fail(...)
|
|
754
771
|
end, function()
|
|
755
|
-
for _, item in
|
|
772
|
+
for _, item in values do
|
|
756
773
|
sub:Fire(item)
|
|
757
774
|
end
|
|
758
775
|
sub:Complete()
|
|
@@ -778,10 +795,10 @@ end
|
|
|
778
795
|
@param predicate (value: T) -> boolean
|
|
779
796
|
@return (source: Observable<T>) -> Observable<T>
|
|
780
797
|
]=]
|
|
781
|
-
function Rx.where(predicate)
|
|
798
|
+
function Rx.where<T...>(predicate: Predicate<T...>): Observable.Transformer<T..., T...>
|
|
782
799
|
assert(type(predicate) == "function", "Bad predicate callback")
|
|
783
800
|
|
|
784
|
-
return function(source)
|
|
801
|
+
return function(source: Observable.Observable<T...>): Observable.Observable<T...>
|
|
785
802
|
assert(Observable.isObservable(source), "Bad observable")
|
|
786
803
|
|
|
787
804
|
return Observable.new(function(sub)
|
|
@@ -806,7 +823,7 @@ end
|
|
|
806
823
|
```
|
|
807
824
|
@return (source: Observable<T>) -> Observable<T>
|
|
808
825
|
]=]
|
|
809
|
-
function Rx.distinct()
|
|
826
|
+
function Rx.distinct<T...>(): Observable.Transformer<T..., T...>
|
|
810
827
|
return function(source)
|
|
811
828
|
assert(Observable.isObservable(source), "Bad observable")
|
|
812
829
|
|
|
@@ -822,7 +839,7 @@ function Rx.distinct()
|
|
|
822
839
|
last = value
|
|
823
840
|
sub:Fire(last)
|
|
824
841
|
end, sub:GetFailComplete())
|
|
825
|
-
end)
|
|
842
|
+
end) :: Observable.Observable<T...>
|
|
826
843
|
end
|
|
827
844
|
end
|
|
828
845
|
|
|
@@ -860,7 +877,7 @@ end
|
|
|
860
877
|
@param project (T) -> U
|
|
861
878
|
@return (source: Observable<T>) -> Observable<U>
|
|
862
879
|
]=]
|
|
863
|
-
function Rx.map(project)
|
|
880
|
+
function Rx.map<T..., U...>(project: (T...) -> U...): Observable.Transformer<T..., U...>
|
|
864
881
|
assert(type(project) == "function", "Bad project callback")
|
|
865
882
|
|
|
866
883
|
return function(source)
|
|
@@ -870,7 +887,7 @@ function Rx.map(project)
|
|
|
870
887
|
return source:Subscribe(function(...)
|
|
871
888
|
sub:Fire(project(...))
|
|
872
889
|
end, sub:GetFailComplete())
|
|
873
|
-
end)
|
|
890
|
+
end) :: Observable.Observable<U...>
|
|
874
891
|
end
|
|
875
892
|
end
|
|
876
893
|
|
|
@@ -891,7 +908,7 @@ end
|
|
|
891
908
|
|
|
892
909
|
@return (source: Observable<Observable<T>>) -> Observable<T>
|
|
893
910
|
]=]
|
|
894
|
-
function Rx.mergeAll()
|
|
911
|
+
function Rx.mergeAll<T...>(): Observable.Transformer<Observable.Observable<T...>, T...>
|
|
895
912
|
return Rx.flatMap(identity)
|
|
896
913
|
end
|
|
897
914
|
|
|
@@ -906,7 +923,7 @@ end
|
|
|
906
923
|
|
|
907
924
|
@return (source: Observable<Observable<T>>) -> Observable<T>
|
|
908
925
|
]=]
|
|
909
|
-
function Rx.switchAll()
|
|
926
|
+
function Rx.switchAll<T...>(): Observable.Transformer<Observable.Observable<T...>, T...>
|
|
910
927
|
return Rx.switchMap(identity)
|
|
911
928
|
end
|
|
912
929
|
|
|
@@ -918,16 +935,16 @@ end
|
|
|
918
935
|
@param project (value: T) -> Observable<U>
|
|
919
936
|
@return (source: Observable<T>) -> Observable<U>
|
|
920
937
|
]=]
|
|
921
|
-
function Rx.flatMap(project)
|
|
938
|
+
function Rx.flatMap<T..., U...>(project: (T...) -> Observable.Observable<U...>): Observable.Transformer<(T...), (U...)>
|
|
922
939
|
assert(type(project) == "function", "Bad project")
|
|
923
940
|
|
|
924
|
-
return function(source)
|
|
941
|
+
return function(source: Observable.Observable<T...>): Observable.Observable<U...>
|
|
925
942
|
assert(Observable.isObservable(source), "Bad observable")
|
|
926
943
|
|
|
927
|
-
return Observable.new(function(sub)
|
|
928
|
-
local isComplete = false
|
|
929
|
-
local pendingCount = 0
|
|
930
|
-
local subscriptions = {}
|
|
944
|
+
return Observable.new(function(sub: _Subscription.Subscription<U...>)
|
|
945
|
+
local isComplete: boolean = false
|
|
946
|
+
local pendingCount: number = 0
|
|
947
|
+
local subscriptions: Set<_Subscription.Subscription<U...>> = {}
|
|
931
948
|
|
|
932
949
|
local function checkComplete()
|
|
933
950
|
if isComplete and pendingCount == 0 then
|
|
@@ -935,8 +952,8 @@ function Rx.flatMap(project)
|
|
|
935
952
|
end
|
|
936
953
|
end
|
|
937
954
|
|
|
938
|
-
local function onNextObservable(...)
|
|
939
|
-
local observable = project(...)
|
|
955
|
+
local function onNextObservable(...: T...)
|
|
956
|
+
local observable: Observable.Observable<U...> = project(...)
|
|
940
957
|
assert(Observable.isObservable(observable), "Bad observable returned from subscription project call")
|
|
941
958
|
|
|
942
959
|
if not sub:IsPending() then
|
|
@@ -945,9 +962,9 @@ function Rx.flatMap(project)
|
|
|
945
962
|
end
|
|
946
963
|
|
|
947
964
|
local innerCompleteOrFail = false
|
|
948
|
-
local subscription
|
|
965
|
+
local subscription: _Subscription.Subscription<U...>? = nil
|
|
949
966
|
|
|
950
|
-
local function onNext(...)
|
|
967
|
+
local function onNext(...: U...)
|
|
951
968
|
if innerCompleteOrFail or pendingCount == 0 then
|
|
952
969
|
return
|
|
953
970
|
end
|
|
@@ -988,15 +1005,16 @@ function Rx.flatMap(project)
|
|
|
988
1005
|
end
|
|
989
1006
|
|
|
990
1007
|
pendingCount += 1
|
|
991
|
-
|
|
1008
|
+
local newSub = observable:Subscribe(onNext, onFail, onComplete)
|
|
1009
|
+
subscription = newSub
|
|
992
1010
|
|
|
993
1011
|
if innerCompleteOrFail or not sub:IsPending() then
|
|
994
1012
|
-- Subscribing cancelled ourselves in some way
|
|
995
|
-
|
|
1013
|
+
newSub:Destroy()
|
|
996
1014
|
return
|
|
997
1015
|
end
|
|
998
1016
|
|
|
999
|
-
subscriptions[
|
|
1017
|
+
subscriptions[newSub] = true
|
|
1000
1018
|
end
|
|
1001
1019
|
|
|
1002
1020
|
local outerSubscription = source:Subscribe(onNextObservable, function(...)
|
|
@@ -1009,13 +1027,13 @@ function Rx.flatMap(project)
|
|
|
1009
1027
|
return function()
|
|
1010
1028
|
pendingCount = 0
|
|
1011
1029
|
|
|
1012
|
-
for subscription
|
|
1030
|
+
for subscription: any in subscriptions do
|
|
1013
1031
|
subscription:Destroy()
|
|
1014
1032
|
end
|
|
1015
1033
|
|
|
1016
1034
|
outerSubscription:Destroy()
|
|
1017
1035
|
end
|
|
1018
|
-
end)
|
|
1036
|
+
end) :: any
|
|
1019
1037
|
end
|
|
1020
1038
|
end
|
|
1021
1039
|
|
|
@@ -1075,17 +1093,17 @@ end
|
|
|
1075
1093
|
@param project function
|
|
1076
1094
|
@return Observable
|
|
1077
1095
|
]=]
|
|
1078
|
-
function Rx.switchMap(project)
|
|
1096
|
+
function Rx.switchMap<T..., U...>(project: (T...) -> Observable.Observable<U...>): Observable.Transformer<(T...), (U...)>
|
|
1079
1097
|
assert(type(project) == "function", "Bad project")
|
|
1080
1098
|
|
|
1081
|
-
return function(source)
|
|
1099
|
+
return function(source: Observable.Observable<T...>)
|
|
1082
1100
|
assert(Observable.isObservable(source), "Bad observable")
|
|
1083
1101
|
|
|
1084
|
-
return Observable.new(function(sub)
|
|
1085
|
-
local isComplete = false
|
|
1086
|
-
local insideComplete = false
|
|
1087
|
-
local insideSubscription = nil
|
|
1088
|
-
local outerIndex = 0
|
|
1102
|
+
return Observable.new(function(sub: _Subscription.Subscription<U...>)
|
|
1103
|
+
local isComplete: boolean = false
|
|
1104
|
+
local insideComplete: boolean = false
|
|
1105
|
+
local insideSubscription: _Subscription.Subscription<T...>? = nil
|
|
1106
|
+
local outerIndex: number? = 0
|
|
1089
1107
|
|
|
1090
1108
|
local function checkComplete()
|
|
1091
1109
|
if isComplete and insideComplete then
|
|
@@ -1097,7 +1115,7 @@ function Rx.switchMap(project)
|
|
|
1097
1115
|
local function onNextObservable(...)
|
|
1098
1116
|
insideComplete = false
|
|
1099
1117
|
|
|
1100
|
-
local observable = project(...)
|
|
1118
|
+
local observable: Observable.Observable<U...> = project(...)
|
|
1101
1119
|
assert(Observable.isObservable(observable), "Bad observable returned from subscription project call")
|
|
1102
1120
|
|
|
1103
1121
|
-- Handle cancellation when external callers do weird state stuff
|
|
@@ -1119,7 +1137,7 @@ function Rx.switchMap(project)
|
|
|
1119
1137
|
return
|
|
1120
1138
|
end
|
|
1121
1139
|
|
|
1122
|
-
local function onNext(...)
|
|
1140
|
+
local function onNext(...: U...)
|
|
1123
1141
|
if index ~= outerIndex then
|
|
1124
1142
|
return
|
|
1125
1143
|
end
|
|
@@ -1155,7 +1173,7 @@ function Rx.switchMap(project)
|
|
|
1155
1173
|
return
|
|
1156
1174
|
end
|
|
1157
1175
|
|
|
1158
|
-
insideSubscription = subscription
|
|
1176
|
+
insideSubscription = subscription :: any
|
|
1159
1177
|
end
|
|
1160
1178
|
|
|
1161
1179
|
local outerSubscription = source:Subscribe(onNextObservable, function(...)
|
|
@@ -1176,7 +1194,7 @@ function Rx.switchMap(project)
|
|
|
1176
1194
|
|
|
1177
1195
|
outerSubscription:Destroy()
|
|
1178
1196
|
end
|
|
1179
|
-
end)
|
|
1197
|
+
end) :: Observable.Observable<U...>
|
|
1180
1198
|
end
|
|
1181
1199
|
end
|
|
1182
1200
|
|
|
@@ -1226,13 +1244,14 @@ end
|
|
|
1226
1244
|
@param ... any
|
|
1227
1245
|
@return Observable
|
|
1228
1246
|
]=]
|
|
1229
|
-
function Rx.packed(...)
|
|
1247
|
+
function Rx.packed<T...>(...: T...): Observable.Observable<T...>
|
|
1230
1248
|
local args = table.pack(...)
|
|
1231
1249
|
|
|
1232
1250
|
return Observable.new(function(sub)
|
|
1233
1251
|
sub:Fire(unpack(args, 1, args.n))
|
|
1234
1252
|
sub:Complete()
|
|
1235
|
-
|
|
1253
|
+
return
|
|
1254
|
+
end) :: any
|
|
1236
1255
|
end
|
|
1237
1256
|
|
|
1238
1257
|
--[=[
|
|
@@ -1419,20 +1438,20 @@ end
|
|
|
1419
1438
|
@param observables { [TKey]: Observable<TEmitted> | TEmitted }
|
|
1420
1439
|
@return Observable<{ [TKey]: TEmitted }>
|
|
1421
1440
|
]=]
|
|
1422
|
-
function Rx.combineLatest(observables)
|
|
1441
|
+
function Rx.combineLatest<K, V>(observables: Map<K, Observable.Observable<V> | V>): Observable.Observable<Map<K, V>>
|
|
1423
1442
|
assert(type(observables) == "table", "Bad observables")
|
|
1424
1443
|
|
|
1425
1444
|
return Observable.new(function(sub)
|
|
1426
1445
|
local unset = 0
|
|
1427
|
-
local latest = {}
|
|
1446
|
+
local latest: Map<K, V> = {}
|
|
1428
1447
|
|
|
1429
1448
|
-- Instead of caching this, use extra compute here
|
|
1430
|
-
for key, value in
|
|
1449
|
+
for key, value in observables do
|
|
1431
1450
|
if Observable.isObservable(value) then
|
|
1432
1451
|
unset += 1
|
|
1433
1452
|
latest[key] = UNSET_VALUE
|
|
1434
1453
|
else
|
|
1435
|
-
latest[key] = value
|
|
1454
|
+
latest[key] = value :: V
|
|
1436
1455
|
end
|
|
1437
1456
|
end
|
|
1438
1457
|
|
|
@@ -1447,19 +1466,19 @@ function Rx.combineLatest(observables)
|
|
|
1447
1466
|
|
|
1448
1467
|
local function failOnFirst(...)
|
|
1449
1468
|
pending -= 1
|
|
1450
|
-
latest = nil
|
|
1469
|
+
latest = nil :: any
|
|
1451
1470
|
sub:Fail(...)
|
|
1452
1471
|
end
|
|
1453
1472
|
|
|
1454
1473
|
local function completeOnAllPendingDone()
|
|
1455
1474
|
pending -= 1
|
|
1456
1475
|
if pending == 0 then
|
|
1457
|
-
latest = nil
|
|
1476
|
+
latest = nil :: any
|
|
1458
1477
|
sub:Complete()
|
|
1459
1478
|
end
|
|
1460
1479
|
end
|
|
1461
1480
|
|
|
1462
|
-
for key, observer in
|
|
1481
|
+
for key, observer: any in observables do
|
|
1463
1482
|
if not Observable.isObservable(observer) then
|
|
1464
1483
|
continue
|
|
1465
1484
|
end
|
|
@@ -1478,7 +1497,7 @@ function Rx.combineLatest(observables)
|
|
|
1478
1497
|
end
|
|
1479
1498
|
|
|
1480
1499
|
return maid
|
|
1481
|
-
end)
|
|
1500
|
+
end) :: any
|
|
1482
1501
|
end
|
|
1483
1502
|
|
|
1484
1503
|
--[=[
|
|
@@ -1495,10 +1514,10 @@ function Rx.combineLatestDefer(observables)
|
|
|
1495
1514
|
return Observable.new(function(sub)
|
|
1496
1515
|
local pending = 0
|
|
1497
1516
|
local unset = 0
|
|
1498
|
-
local latest = {}
|
|
1517
|
+
local latest: any = {}
|
|
1499
1518
|
|
|
1500
1519
|
-- Instead of caching this, use extra compute here
|
|
1501
|
-
for key, value in
|
|
1520
|
+
for key, value in observables do
|
|
1502
1521
|
if Observable.isObservable(value) then
|
|
1503
1522
|
pending += 1
|
|
1504
1523
|
unset += 1
|
|
@@ -1530,14 +1549,14 @@ function Rx.combineLatestDefer(observables)
|
|
|
1530
1549
|
end
|
|
1531
1550
|
end
|
|
1532
1551
|
|
|
1533
|
-
local queueThread = nil
|
|
1552
|
+
local queueThread: thread? = nil
|
|
1534
1553
|
maid:GiveTask(function()
|
|
1535
1554
|
if queueThread then
|
|
1536
1555
|
MaidTaskUtils.doTask(queueThread)
|
|
1537
1556
|
end
|
|
1538
1557
|
end)
|
|
1539
1558
|
|
|
1540
|
-
for key, observer in
|
|
1559
|
+
for key, observer in observables do
|
|
1541
1560
|
if not Observable.isObservable(observer) then
|
|
1542
1561
|
continue
|
|
1543
1562
|
end
|
|
@@ -1614,7 +1633,7 @@ end
|
|
|
1614
1633
|
@param number number
|
|
1615
1634
|
@return (source: Observable<T>) -> Observable<T>
|
|
1616
1635
|
]=]
|
|
1617
|
-
function Rx.take(number: number)
|
|
1636
|
+
function Rx.take<T...>(number: number): Observable.Transformer<T..., T...>
|
|
1618
1637
|
assert(type(number) == "number", "Bad number")
|
|
1619
1638
|
assert(number > 0, "Bad number")
|
|
1620
1639
|
|
|
@@ -1643,7 +1662,7 @@ function Rx.take(number: number)
|
|
|
1643
1662
|
end, sub:GetFailComplete())
|
|
1644
1663
|
|
|
1645
1664
|
return maid
|
|
1646
|
-
end)
|
|
1665
|
+
end) :: Observable.Observable<T...>
|
|
1647
1666
|
end
|
|
1648
1667
|
end
|
|
1649
1668
|
|
|
@@ -1654,7 +1673,7 @@ end
|
|
|
1654
1673
|
@param toSkip number
|
|
1655
1674
|
@return (source: Observable<T>) -> Observable<T>
|
|
1656
1675
|
]=]
|
|
1657
|
-
function Rx.skip(toSkip: number)
|
|
1676
|
+
function Rx.skip<T...>(toSkip: number): Observable.Transformer<T..., T...>
|
|
1658
1677
|
assert(type(toSkip) == "number", "Bad toSkip")
|
|
1659
1678
|
assert(toSkip > 0, "Bad toSkip")
|
|
1660
1679
|
|
|
@@ -1675,7 +1694,7 @@ function Rx.skip(toSkip: number)
|
|
|
1675
1694
|
end, sub:GetFailComplete()))
|
|
1676
1695
|
|
|
1677
1696
|
return maid
|
|
1678
|
-
end)
|
|
1697
|
+
end) :: Observable.Observable<T...>
|
|
1679
1698
|
end
|
|
1680
1699
|
end
|
|
1681
1700
|
|
|
@@ -1689,7 +1708,7 @@ end
|
|
|
1689
1708
|
@param observableFactory () -> Observable<T>
|
|
1690
1709
|
@return Observable<T>
|
|
1691
1710
|
]=]
|
|
1692
|
-
function Rx.defer(observableFactory)
|
|
1711
|
+
function Rx.defer<T>(observableFactory: () -> Observable.Observable<T>): Observable.Observable<T>
|
|
1693
1712
|
return Observable.new(function(sub)
|
|
1694
1713
|
local observable
|
|
1695
1714
|
local ok, err = pcall(function()
|
|
@@ -1707,7 +1726,7 @@ function Rx.defer(observableFactory)
|
|
|
1707
1726
|
end
|
|
1708
1727
|
|
|
1709
1728
|
return observable:Subscribe(sub:GetFireFailComplete())
|
|
1710
|
-
end)
|
|
1729
|
+
end) :: any
|
|
1711
1730
|
end
|
|
1712
1731
|
|
|
1713
1732
|
--[=[
|
|
@@ -1716,13 +1735,13 @@ end
|
|
|
1716
1735
|
@param seconds number
|
|
1717
1736
|
@return (source: Observable<T>) -> Observable<T>
|
|
1718
1737
|
]=]
|
|
1719
|
-
function Rx.delay(seconds: number)
|
|
1738
|
+
function Rx.delay<T...>(seconds: number): Observable.Transformer<T..., T...>
|
|
1720
1739
|
assert(type(seconds) == "number", "Bad seconds")
|
|
1721
1740
|
|
|
1722
1741
|
return function(source)
|
|
1723
1742
|
assert(Observable.isObservable(source), "Bad observable")
|
|
1724
1743
|
|
|
1725
|
-
return Observable.new(function(sub)
|
|
1744
|
+
return Observable.new(function(sub: _Subscription.Subscription<T...>)
|
|
1726
1745
|
local maid = Maid.new()
|
|
1727
1746
|
|
|
1728
1747
|
maid:GiveTask(source:Subscribe(function(...)
|
|
@@ -1735,7 +1754,7 @@ function Rx.delay(seconds: number)
|
|
|
1735
1754
|
end, sub:GetFailComplete()))
|
|
1736
1755
|
|
|
1737
1756
|
return maid
|
|
1738
|
-
end)
|
|
1757
|
+
end) :: Observable.Observable<T...>
|
|
1739
1758
|
end
|
|
1740
1759
|
end
|
|
1741
1760
|
|
|
@@ -1811,7 +1830,7 @@ end
|
|
|
1811
1830
|
function Rx.withLatestFrom(inputObservables)
|
|
1812
1831
|
assert(inputObservables, "Bad inputObservables")
|
|
1813
1832
|
|
|
1814
|
-
for _, observable in
|
|
1833
|
+
for _, observable in inputObservables do
|
|
1815
1834
|
assert(Observable.isObservable(observable), "Bad observable")
|
|
1816
1835
|
end
|
|
1817
1836
|
|
|
@@ -1823,7 +1842,7 @@ function Rx.withLatestFrom(inputObservables)
|
|
|
1823
1842
|
|
|
1824
1843
|
local latest = {}
|
|
1825
1844
|
|
|
1826
|
-
for key, observable in
|
|
1845
|
+
for key, observable in inputObservables do
|
|
1827
1846
|
latest[key] = UNSET_VALUE
|
|
1828
1847
|
|
|
1829
1848
|
maid:GiveTask(observable:Subscribe(function(value)
|
|
@@ -1832,7 +1851,7 @@ function Rx.withLatestFrom(inputObservables)
|
|
|
1832
1851
|
end
|
|
1833
1852
|
|
|
1834
1853
|
maid:GiveTask(source:Subscribe(function(value)
|
|
1835
|
-
for _, item in
|
|
1854
|
+
for _, item in latest do
|
|
1836
1855
|
if item == UNSET_VALUE then
|
|
1837
1856
|
return
|
|
1838
1857
|
end
|
|
@@ -1859,7 +1878,7 @@ end
|
|
|
1859
1878
|
@param throttleConfig { leading = true; trailing = true; }
|
|
1860
1879
|
@return (source: Observable) -> Observable
|
|
1861
1880
|
]=]
|
|
1862
|
-
function Rx.throttleTime(duration: number, throttleConfig)
|
|
1881
|
+
function Rx.throttleTime(duration: number, throttleConfig: ThrottledFunction.ThrottleConfig)
|
|
1863
1882
|
assert(type(duration) == "number", "Bad duration")
|
|
1864
1883
|
assert(type(throttleConfig) == "table" or throttleConfig == nil, "Bad throttleConfig")
|
|
1865
1884
|
|
|
@@ -1920,8 +1939,8 @@ function Rx.throttleDefer()
|
|
|
1920
1939
|
assert(Observable.isObservable(source), "Bad observable")
|
|
1921
1940
|
|
|
1922
1941
|
return Observable.new(function(sub)
|
|
1923
|
-
local lastResult = nil
|
|
1924
|
-
local currentQueue = nil
|
|
1942
|
+
local lastResult: any = nil
|
|
1943
|
+
local currentQueue: thread? = nil
|
|
1925
1944
|
|
|
1926
1945
|
local sourceSub = source:Subscribe(function(...)
|
|
1927
1946
|
if lastResult then
|
|
@@ -1972,7 +1991,7 @@ function Rx.throttle(durationSelector)
|
|
|
1972
1991
|
return Observable.new(function(sub)
|
|
1973
1992
|
local topMaid = Maid.new()
|
|
1974
1993
|
|
|
1975
|
-
local lastResult
|
|
1994
|
+
local lastResult: any
|
|
1976
1995
|
|
|
1977
1996
|
topMaid:GiveTask(source:Subscribe(function(...)
|
|
1978
1997
|
if not lastResult then
|
|
@@ -2115,7 +2134,7 @@ function Rx.switchScan(accumulator, seed)
|
|
|
2115
2134
|
assert(type(accumulator) == "function", "Bad accumulator")
|
|
2116
2135
|
|
|
2117
2136
|
return Rx.pipe({
|
|
2118
|
-
Rx.scan(accumulator, seed),
|
|
2137
|
+
Rx.scan(accumulator, seed) :: any,
|
|
2119
2138
|
Rx.switchAll(),
|
|
2120
2139
|
})
|
|
2121
2140
|
end
|
|
@@ -2134,7 +2153,7 @@ function Rx.mergeScan(accumulator, seed)
|
|
|
2134
2153
|
assert(type(accumulator) == "function", "Bad accumulator")
|
|
2135
2154
|
|
|
2136
2155
|
return Rx.pipe({
|
|
2137
|
-
Rx.scan(accumulator, seed),
|
|
2156
|
+
Rx.scan(accumulator, seed) :: any,
|
|
2138
2157
|
Rx.mergeAll(),
|
|
2139
2158
|
})
|
|
2140
2159
|
end
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
--!strict
|
|
1
2
|
--[=[
|
|
2
3
|
Subscriptions are used in the callback for an [Observable](/api/Observable). Standard usage
|
|
3
4
|
is as follows.
|
|
@@ -24,13 +25,40 @@ local Subscription = {}
|
|
|
24
25
|
Subscription.ClassName = "Subscription"
|
|
25
26
|
Subscription.__index = Subscription
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
export type SubscriptionState = "pending" | "failed" | "complete" | "cancelled"
|
|
29
|
+
|
|
30
|
+
export type FireCallback<T...> = (T...) -> ()
|
|
31
|
+
export type CompleteCallback = () -> ()
|
|
32
|
+
export type FailCallback = (...any) -> ()
|
|
33
|
+
|
|
34
|
+
export type Subscription<T...> = typeof(setmetatable(
|
|
35
|
+
{} :: {
|
|
36
|
+
_state: SubscriptionState,
|
|
37
|
+
_cleanupTask: MaidTaskUtils.MaidTask?,
|
|
38
|
+
_fireCallback: FireCallback<T...>?,
|
|
39
|
+
_failCallback: FailCallback?,
|
|
40
|
+
_completeCallback: CompleteCallback?,
|
|
41
|
+
_fireCountAfterDeath: number?,
|
|
42
|
+
_source: string?,
|
|
43
|
+
_observableSource: string?,
|
|
44
|
+
},
|
|
45
|
+
Subscription
|
|
46
|
+
))
|
|
47
|
+
|
|
48
|
+
export type SubscriptionStateTypes = {
|
|
49
|
+
PENDING: "pending",
|
|
50
|
+
FAILED: "failed",
|
|
51
|
+
COMPLETE: "complete",
|
|
52
|
+
CANCELLED: "cancelled",
|
|
32
53
|
}
|
|
33
54
|
|
|
55
|
+
local SubscriptionStateTypes: SubscriptionStateTypes = table.freeze({
|
|
56
|
+
PENDING = "pending",
|
|
57
|
+
FAILED = "failed",
|
|
58
|
+
COMPLETE = "complete",
|
|
59
|
+
CANCELLED = "cancelled",
|
|
60
|
+
} :: SubscriptionStateTypes)
|
|
61
|
+
|
|
34
62
|
--[=[
|
|
35
63
|
Constructs a new Subscription
|
|
36
64
|
|
|
@@ -40,18 +68,23 @@ local SubscriptionStateTypes = {
|
|
|
40
68
|
@param observableSource string?
|
|
41
69
|
@return Subscription
|
|
42
70
|
]=]
|
|
43
|
-
function Subscription.new(
|
|
71
|
+
function Subscription.new<T...>(
|
|
72
|
+
fireCallback: FireCallback<T...>?,
|
|
73
|
+
failCallback: FailCallback?,
|
|
74
|
+
completeCallback: CompleteCallback?,
|
|
75
|
+
observableSource
|
|
76
|
+
): Subscription<T...>
|
|
44
77
|
assert(type(fireCallback) == "function" or fireCallback == nil, "Bad fireCallback")
|
|
45
78
|
assert(type(failCallback) == "function" or failCallback == nil, "Bad failCallback")
|
|
46
79
|
assert(type(completeCallback) == "function" or completeCallback == nil, "Bad completeCallback")
|
|
47
80
|
|
|
48
81
|
return setmetatable({
|
|
49
|
-
_state = SubscriptionStateTypes.PENDING
|
|
50
|
-
_source = if ENABLE_STACK_TRACING then debug.traceback("Subscription.new()", 3) else nil
|
|
51
|
-
_observableSource = observableSource
|
|
52
|
-
_fireCallback = fireCallback
|
|
53
|
-
_failCallback = failCallback
|
|
54
|
-
_completeCallback = completeCallback
|
|
82
|
+
_state = SubscriptionStateTypes.PENDING,
|
|
83
|
+
_source = if ENABLE_STACK_TRACING then debug.traceback("Subscription.new()", 3) else nil,
|
|
84
|
+
_observableSource = observableSource,
|
|
85
|
+
_fireCallback = fireCallback,
|
|
86
|
+
_failCallback = failCallback,
|
|
87
|
+
_completeCallback = completeCallback,
|
|
55
88
|
}, Subscription)
|
|
56
89
|
end
|
|
57
90
|
|
|
@@ -60,7 +93,7 @@ end
|
|
|
60
93
|
|
|
61
94
|
@param ... any
|
|
62
95
|
]=]
|
|
63
|
-
function Subscription
|
|
96
|
+
function Subscription.Fire<T...>(self: Subscription<T...>, ...: T...)
|
|
64
97
|
if self._state == SubscriptionStateTypes.PENDING then
|
|
65
98
|
if self._fireCallback then
|
|
66
99
|
self._fireCallback(...)
|
|
@@ -73,7 +106,16 @@ function Subscription:Fire(...)
|
|
|
73
106
|
end
|
|
74
107
|
|
|
75
108
|
if self._fireCountAfterDeath > 1 then
|
|
76
|
-
warn(
|
|
109
|
+
warn(
|
|
110
|
+
debug.traceback(
|
|
111
|
+
string.format(
|
|
112
|
+
"Subscription:Fire(%s) called %d times after death. Be sure to disconnect all events.",
|
|
113
|
+
(tostring :: any)(...),
|
|
114
|
+
self._fireCountAfterDeath or -1
|
|
115
|
+
),
|
|
116
|
+
2
|
|
117
|
+
)
|
|
118
|
+
)
|
|
77
119
|
|
|
78
120
|
if ENABLE_STACK_TRACING then
|
|
79
121
|
print(self._observableSource)
|
|
@@ -87,7 +129,7 @@ end
|
|
|
87
129
|
Fails the subscription, preventing anything else from emitting.
|
|
88
130
|
@param ... any
|
|
89
131
|
]=]
|
|
90
|
-
function Subscription
|
|
132
|
+
function Subscription.Fail<T...>(self: Subscription<T...>, ...: any)
|
|
91
133
|
if self._state ~= SubscriptionStateTypes.PENDING then
|
|
92
134
|
return
|
|
93
135
|
end
|
|
@@ -101,7 +143,6 @@ function Subscription:Fail(...)
|
|
|
101
143
|
self:_doCleanup()
|
|
102
144
|
end
|
|
103
145
|
|
|
104
|
-
|
|
105
146
|
--[=[
|
|
106
147
|
Returns a tuple of fire, fail and complete functions which
|
|
107
148
|
can be chained into the the next subscription.
|
|
@@ -120,8 +161,10 @@ end
|
|
|
120
161
|
@return function
|
|
121
162
|
@return function
|
|
122
163
|
]=]
|
|
123
|
-
function Subscription
|
|
124
|
-
|
|
164
|
+
function Subscription.GetFireFailComplete<T...>(
|
|
165
|
+
self: Subscription<T...>
|
|
166
|
+
): (FireCallback<T...>, FailCallback, CompleteCallback)
|
|
167
|
+
return function(...: T...)
|
|
125
168
|
self:Fire(...)
|
|
126
169
|
end, function(...)
|
|
127
170
|
self:Fail(...)
|
|
@@ -147,7 +190,7 @@ end
|
|
|
147
190
|
@return function
|
|
148
191
|
@return function
|
|
149
192
|
]=]
|
|
150
|
-
function Subscription
|
|
193
|
+
function Subscription.GetFailComplete<T...>(self: Subscription<T...>): (FailCallback, CompleteCallback)
|
|
151
194
|
return function(...)
|
|
152
195
|
self:Fail(...)
|
|
153
196
|
end, function(...)
|
|
@@ -161,7 +204,7 @@ end
|
|
|
161
204
|
|
|
162
205
|
@param ... any
|
|
163
206
|
]=]
|
|
164
|
-
function Subscription
|
|
207
|
+
function Subscription.Complete<T...>(self: Subscription<T...>, ...)
|
|
165
208
|
if self._state ~= SubscriptionStateTypes.PENDING then
|
|
166
209
|
return
|
|
167
210
|
end
|
|
@@ -178,11 +221,11 @@ end
|
|
|
178
221
|
Returns whether the subscription is pending.
|
|
179
222
|
@return boolean
|
|
180
223
|
]=]
|
|
181
|
-
function Subscription
|
|
224
|
+
function Subscription.IsPending<T...>(self: Subscription<T...>): boolean
|
|
182
225
|
return self._state == SubscriptionStateTypes.PENDING
|
|
183
226
|
end
|
|
184
227
|
|
|
185
|
-
function Subscription
|
|
228
|
+
function Subscription._assignCleanup<T...>(self: Subscription<T...>, cleanupTask: MaidTaskUtils.MaidTask?)
|
|
186
229
|
assert(self._cleanupTask == nil, "Already have _cleanupTask")
|
|
187
230
|
|
|
188
231
|
if MaidTaskUtils.isValidTask(cleanupTask) then
|
|
@@ -197,7 +240,7 @@ function Subscription:_assignCleanup(cleanupTask)
|
|
|
197
240
|
end
|
|
198
241
|
end
|
|
199
242
|
|
|
200
|
-
function Subscription
|
|
243
|
+
function Subscription._doCleanup<T...>(self: Subscription<T...>)
|
|
201
244
|
local cleanupTask = self._cleanupTask
|
|
202
245
|
if cleanupTask then
|
|
203
246
|
self._cleanupTask = nil
|
|
@@ -221,7 +264,7 @@ end
|
|
|
221
264
|
be called within the usage of a subscription.
|
|
222
265
|
:::
|
|
223
266
|
]=]
|
|
224
|
-
function Subscription
|
|
267
|
+
function Subscription.Destroy<T...>(self: Subscription<T...>)
|
|
225
268
|
if self._state == SubscriptionStateTypes.PENDING then
|
|
226
269
|
self._state = SubscriptionStateTypes.CANCELLED
|
|
227
270
|
end
|
|
@@ -231,9 +274,10 @@ end
|
|
|
231
274
|
|
|
232
275
|
--[=[
|
|
233
276
|
Alias for [Subscription.Destroy].
|
|
277
|
+
|
|
278
|
+
@method Disconnect
|
|
279
|
+
@within Subscription
|
|
234
280
|
]=]
|
|
235
|
-
|
|
236
|
-
self:Destroy()
|
|
237
|
-
end
|
|
281
|
+
Subscription.Disconnect = Subscription.Destroy
|
|
238
282
|
|
|
239
283
|
return Subscription
|