@quenty/binder 14.40.0 → 14.41.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 +6 -0
- package/package.json +13 -13
- package/src/Shared/Binder.Loading.spec.lua +54 -0
- package/src/Shared/Binder.ObserveBrio.spec.lua +209 -0
- package/src/Shared/Binder.spec.lua +120 -255
- package/src/Shared/BinderGroup.spec.lua +19 -24
- package/src/Shared/BinderTestUtils.lua +163 -0
- package/src/Shared/BinderUtils.spec.lua +94 -103
- package/src/Shared/Collection/BoundChildCollection.spec.lua +36 -45
- package/src/Shared/Promise/promiseBoundClass.spec.lua +21 -27
- package/src/Shared/Trackers/BoundAncestorTracker.spec.lua +36 -44
- package/src/Shared/Trackers/BoundParentTracker.spec.lua +28 -36
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
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
|
+
# [14.41.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/binder@14.40.0...@quenty/binder@14.41.0) (2026-07-23)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
- Add baseline player-mock and support across Nevermore for mocked players. ([567d121](https://github.com/Quenty/NevermoreEngine/commit/567d121ffc014b42391554088189a1a6296dda83))
|
|
11
|
+
|
|
6
12
|
# [14.40.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/binder@14.39.0...@quenty/binder@14.40.0) (2026-07-22)
|
|
7
13
|
|
|
8
14
|
**Note:** Version bump only for package @quenty/binder
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quenty/binder",
|
|
3
|
-
"version": "14.
|
|
3
|
+
"version": "14.41.0",
|
|
4
4
|
"description": "Utility object to Bind a class to Roblox object, and associated helper methods",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Roblox",
|
|
@@ -28,24 +28,24 @@
|
|
|
28
28
|
"Quenty"
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@quenty/baseobject": "10.
|
|
32
|
-
"@quenty/brio": "14.
|
|
33
|
-
"@quenty/canceltoken": "11.
|
|
34
|
-
"@quenty/instanceutils": "13.
|
|
35
|
-
"@quenty/linkutils": "13.
|
|
31
|
+
"@quenty/baseobject": "10.15.0",
|
|
32
|
+
"@quenty/brio": "14.33.0",
|
|
33
|
+
"@quenty/canceltoken": "11.22.0",
|
|
34
|
+
"@quenty/instanceutils": "13.33.0",
|
|
35
|
+
"@quenty/linkutils": "13.34.0",
|
|
36
36
|
"@quenty/loader": "10.11.0",
|
|
37
|
-
"@quenty/maid": "3.
|
|
38
|
-
"@quenty/nevermore-test-runner": "1.
|
|
39
|
-
"@quenty/promise": "10.
|
|
40
|
-
"@quenty/rx": "13.
|
|
41
|
-
"@quenty/servicebag": "11.
|
|
37
|
+
"@quenty/maid": "3.11.0",
|
|
38
|
+
"@quenty/nevermore-test-runner": "1.5.0",
|
|
39
|
+
"@quenty/promise": "10.21.0",
|
|
40
|
+
"@quenty/rx": "13.31.0",
|
|
41
|
+
"@quenty/servicebag": "11.20.0",
|
|
42
42
|
"@quenty/signal": "7.13.1",
|
|
43
43
|
"@quenty/table": "3.9.2",
|
|
44
|
-
"@quenty/valueobject": "13.
|
|
44
|
+
"@quenty/valueobject": "13.34.0",
|
|
45
45
|
"@quentystudios/jest-lua": "3.10.0-quenty.2"
|
|
46
46
|
},
|
|
47
47
|
"publishConfig": {
|
|
48
48
|
"access": "public"
|
|
49
49
|
},
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "1de37218a2bedb8e3f8614a2e09bba9eddc812da"
|
|
51
51
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--[[
|
|
3
|
+
@class Binder.Loading.spec.lua
|
|
4
|
+
]]
|
|
5
|
+
|
|
6
|
+
local require = require(script.Parent.loader).load(script)
|
|
7
|
+
|
|
8
|
+
local BinderTestUtils = require("BinderTestUtils")
|
|
9
|
+
local Jest = require("Jest")
|
|
10
|
+
local Signal = require("Signal")
|
|
11
|
+
|
|
12
|
+
local describe = Jest.Globals.describe
|
|
13
|
+
local expect = Jest.Globals.expect
|
|
14
|
+
local it = Jest.Globals.it
|
|
15
|
+
|
|
16
|
+
local setup = BinderTestUtils.setup
|
|
17
|
+
|
|
18
|
+
describe("Binder load abandonment", function()
|
|
19
|
+
it("does not bind a class when the instance is removed while the constructor yields", function()
|
|
20
|
+
local controller = setup()
|
|
21
|
+
|
|
22
|
+
local entered = Signal.new()
|
|
23
|
+
local resume = Signal.new()
|
|
24
|
+
local constructed = 0
|
|
25
|
+
|
|
26
|
+
local binder = controller.addBinder(function(inst)
|
|
27
|
+
constructed += 1
|
|
28
|
+
entered:Fire()
|
|
29
|
+
resume:Wait()
|
|
30
|
+
return { instance = inst }
|
|
31
|
+
end)
|
|
32
|
+
controller.boot()
|
|
33
|
+
|
|
34
|
+
local inst = controller.newInstance()
|
|
35
|
+
binder:Tag(inst)
|
|
36
|
+
|
|
37
|
+
-- The constructor may run synchronously on Tag or be deferred; wait until it has entered.
|
|
38
|
+
if constructed == 0 then
|
|
39
|
+
entered:Wait()
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
expect(constructed).toEqual(1)
|
|
43
|
+
expect(binder:Get(inst)).toBeNil()
|
|
44
|
+
|
|
45
|
+
binder:Untag(inst)
|
|
46
|
+
resume:Fire()
|
|
47
|
+
task.wait()
|
|
48
|
+
|
|
49
|
+
expect(binder:Get(inst)).toBeNil()
|
|
50
|
+
expect(constructed).toEqual(1)
|
|
51
|
+
|
|
52
|
+
controller.destroy()
|
|
53
|
+
end)
|
|
54
|
+
end)
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--[[
|
|
3
|
+
@class Binder.ObserveBrio.spec.lua
|
|
4
|
+
]]
|
|
5
|
+
|
|
6
|
+
local require = require(script.Parent.loader).load(script)
|
|
7
|
+
|
|
8
|
+
local BinderTestUtils = require("BinderTestUtils")
|
|
9
|
+
local Brio = require("Brio")
|
|
10
|
+
local Jest = require("Jest")
|
|
11
|
+
|
|
12
|
+
local describe = Jest.Globals.describe
|
|
13
|
+
local expect = Jest.Globals.expect
|
|
14
|
+
local it = Jest.Globals.it
|
|
15
|
+
|
|
16
|
+
local setup = BinderTestUtils.setup
|
|
17
|
+
local makeTrackingClass = BinderTestUtils.makeTrackingClass
|
|
18
|
+
local awaitUnbound = BinderTestUtils.awaitUnbound
|
|
19
|
+
|
|
20
|
+
describe("Binder:ObserveBrio()", function()
|
|
21
|
+
it("emits a live brio wrapping the class on bind", function()
|
|
22
|
+
local controller = setup()
|
|
23
|
+
|
|
24
|
+
local binder = controller.addBinder(makeTrackingClass())
|
|
25
|
+
controller.boot()
|
|
26
|
+
|
|
27
|
+
local inst = controller.newInstance()
|
|
28
|
+
|
|
29
|
+
local brios = {}
|
|
30
|
+
local sub = binder:ObserveBrio(inst):Subscribe(function(brio: Brio.Brio<any>)
|
|
31
|
+
table.insert(brios, brio)
|
|
32
|
+
end)
|
|
33
|
+
|
|
34
|
+
expect(#brios).toEqual(0)
|
|
35
|
+
|
|
36
|
+
binder:Tag(inst)
|
|
37
|
+
local ok, class = binder:Promise(inst):Yield()
|
|
38
|
+
assert(ok, "Never bound")
|
|
39
|
+
|
|
40
|
+
expect(#brios).toEqual(1)
|
|
41
|
+
expect(Brio.isBrio(brios[1])).toEqual(true)
|
|
42
|
+
expect(brios[1]:IsDead()).toEqual(false)
|
|
43
|
+
expect(brios[1]:GetValue()).toEqual(class)
|
|
44
|
+
|
|
45
|
+
sub:Destroy()
|
|
46
|
+
controller.destroy()
|
|
47
|
+
end)
|
|
48
|
+
|
|
49
|
+
it("emits immediately when the instance is already bound", function()
|
|
50
|
+
local controller = setup()
|
|
51
|
+
|
|
52
|
+
local binder = controller.addBinder(makeTrackingClass())
|
|
53
|
+
controller.boot()
|
|
54
|
+
|
|
55
|
+
local inst = controller.newInstance()
|
|
56
|
+
binder:Tag(inst)
|
|
57
|
+
local ok, class = binder:Promise(inst):Yield()
|
|
58
|
+
assert(ok, "Never bound")
|
|
59
|
+
|
|
60
|
+
local brios = {}
|
|
61
|
+
local sub = binder:ObserveBrio(inst):Subscribe(function(brio: Brio.Brio<any>)
|
|
62
|
+
table.insert(brios, brio)
|
|
63
|
+
end)
|
|
64
|
+
|
|
65
|
+
expect(#brios).toEqual(1)
|
|
66
|
+
expect(brios[1]:IsDead()).toEqual(false)
|
|
67
|
+
expect(brios[1]:GetValue()).toEqual(class)
|
|
68
|
+
|
|
69
|
+
sub:Destroy()
|
|
70
|
+
controller.destroy()
|
|
71
|
+
end)
|
|
72
|
+
|
|
73
|
+
it("kills the emitted brio when the instance unbinds", function()
|
|
74
|
+
local controller = setup()
|
|
75
|
+
|
|
76
|
+
local binder = controller.addBinder(makeTrackingClass())
|
|
77
|
+
controller.boot()
|
|
78
|
+
|
|
79
|
+
local inst = controller.newInstance()
|
|
80
|
+
|
|
81
|
+
local brios = {}
|
|
82
|
+
local sub = binder:ObserveBrio(inst):Subscribe(function(brio: Brio.Brio<any>)
|
|
83
|
+
table.insert(brios, brio)
|
|
84
|
+
end)
|
|
85
|
+
|
|
86
|
+
binder:Tag(inst)
|
|
87
|
+
assert((binder:Promise(inst):Yield()), "Never bound")
|
|
88
|
+
expect(brios[1]:IsDead()).toEqual(false)
|
|
89
|
+
|
|
90
|
+
binder:Untag(inst)
|
|
91
|
+
awaitUnbound(binder, inst)
|
|
92
|
+
|
|
93
|
+
expect(brios[1]:IsDead()).toEqual(true)
|
|
94
|
+
|
|
95
|
+
sub:Destroy()
|
|
96
|
+
controller.destroy()
|
|
97
|
+
end)
|
|
98
|
+
|
|
99
|
+
it("emits a fresh live brio when the instance rebinds", function()
|
|
100
|
+
local controller = setup()
|
|
101
|
+
|
|
102
|
+
local binder = controller.addBinder(makeTrackingClass())
|
|
103
|
+
controller.boot()
|
|
104
|
+
|
|
105
|
+
local inst = controller.newInstance()
|
|
106
|
+
|
|
107
|
+
local brios = {}
|
|
108
|
+
local sub = binder:ObserveBrio(inst):Subscribe(function(brio: Brio.Brio<any>)
|
|
109
|
+
table.insert(brios, brio)
|
|
110
|
+
end)
|
|
111
|
+
|
|
112
|
+
binder:Tag(inst)
|
|
113
|
+
assert((binder:Promise(inst):Yield()), "Never bound")
|
|
114
|
+
|
|
115
|
+
binder:Untag(inst)
|
|
116
|
+
awaitUnbound(binder, inst)
|
|
117
|
+
|
|
118
|
+
binder:Tag(inst)
|
|
119
|
+
local ok, class = binder:Promise(inst):Yield()
|
|
120
|
+
assert(ok, "Never rebound")
|
|
121
|
+
|
|
122
|
+
expect(#brios).toEqual(2)
|
|
123
|
+
expect(brios[1]:IsDead()).toEqual(true)
|
|
124
|
+
expect(brios[2]:IsDead()).toEqual(false)
|
|
125
|
+
expect(brios[2]:GetValue()).toEqual(class)
|
|
126
|
+
|
|
127
|
+
sub:Destroy()
|
|
128
|
+
controller.destroy()
|
|
129
|
+
end)
|
|
130
|
+
end)
|
|
131
|
+
|
|
132
|
+
describe("Binder:ObserveAllBrio()", function()
|
|
133
|
+
it("emits a live brio for every already-bound class on subscribe", function()
|
|
134
|
+
local controller = setup()
|
|
135
|
+
|
|
136
|
+
local binder = controller.addBinder(makeTrackingClass())
|
|
137
|
+
controller.boot()
|
|
138
|
+
|
|
139
|
+
local instA = controller.newInstance()
|
|
140
|
+
local instB = controller.newInstance()
|
|
141
|
+
binder:Tag(instA)
|
|
142
|
+
binder:Tag(instB)
|
|
143
|
+
assert((binder:Promise(instA):Yield()), "A never bound")
|
|
144
|
+
assert((binder:Promise(instB):Yield()), "B never bound")
|
|
145
|
+
|
|
146
|
+
local values = {}
|
|
147
|
+
local sub = binder:ObserveAllBrio():Subscribe(function(brio: Brio.Brio<any>)
|
|
148
|
+
expect(brio:IsDead()).toEqual(false)
|
|
149
|
+
values[brio:GetValue()] = true
|
|
150
|
+
end)
|
|
151
|
+
|
|
152
|
+
expect(values[binder:Get(instA)]).toEqual(true)
|
|
153
|
+
expect(values[binder:Get(instB)]).toEqual(true)
|
|
154
|
+
|
|
155
|
+
sub:Destroy()
|
|
156
|
+
controller.destroy()
|
|
157
|
+
end)
|
|
158
|
+
|
|
159
|
+
it("emits a brio when a new class is added", function()
|
|
160
|
+
local controller = setup()
|
|
161
|
+
|
|
162
|
+
local binder = controller.addBinder(makeTrackingClass())
|
|
163
|
+
controller.boot()
|
|
164
|
+
|
|
165
|
+
local brios = {}
|
|
166
|
+
local sub = binder:ObserveAllBrio():Subscribe(function(brio: Brio.Brio<any>)
|
|
167
|
+
table.insert(brios, brio)
|
|
168
|
+
end)
|
|
169
|
+
|
|
170
|
+
expect(#brios).toEqual(0)
|
|
171
|
+
|
|
172
|
+
local inst = controller.newInstance()
|
|
173
|
+
binder:Tag(inst)
|
|
174
|
+
assert((binder:Promise(inst):Yield()), "Never bound")
|
|
175
|
+
|
|
176
|
+
expect(#brios).toEqual(1)
|
|
177
|
+
expect(brios[1]:IsDead()).toEqual(false)
|
|
178
|
+
expect(brios[1]:GetValue()).toEqual(binder:Get(inst))
|
|
179
|
+
|
|
180
|
+
sub:Destroy()
|
|
181
|
+
controller.destroy()
|
|
182
|
+
end)
|
|
183
|
+
|
|
184
|
+
it("kills the brio when the class is removed", function()
|
|
185
|
+
local controller = setup()
|
|
186
|
+
|
|
187
|
+
local binder = controller.addBinder(makeTrackingClass())
|
|
188
|
+
controller.boot()
|
|
189
|
+
|
|
190
|
+
local inst = controller.newInstance()
|
|
191
|
+
binder:Tag(inst)
|
|
192
|
+
assert((binder:Promise(inst):Yield()), "Never bound")
|
|
193
|
+
|
|
194
|
+
local brios = {}
|
|
195
|
+
local sub = binder:ObserveAllBrio():Subscribe(function(brio: Brio.Brio<any>)
|
|
196
|
+
table.insert(brios, brio)
|
|
197
|
+
end)
|
|
198
|
+
|
|
199
|
+
expect(#brios).toEqual(1)
|
|
200
|
+
|
|
201
|
+
binder:Untag(inst)
|
|
202
|
+
awaitUnbound(binder, inst)
|
|
203
|
+
|
|
204
|
+
expect(brios[1]:IsDead()).toEqual(true)
|
|
205
|
+
|
|
206
|
+
sub:Destroy()
|
|
207
|
+
controller.destroy()
|
|
208
|
+
end)
|
|
209
|
+
end)
|