@quenty/tie 10.6.0 → 10.7.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +4 -0
  3. package/package.json +16 -15
  4. package/src/Shared/Members/Methods/TieMethodDefinition.lua +44 -0
  5. package/src/Shared/{Implementation → Members/Methods}/TieMethodImplementation.lua +4 -5
  6. package/src/Shared/Members/Methods/TieMethodInterfaceUtils.lua +58 -0
  7. package/src/Shared/{Interface → Members/Properties}/TiePropertyChangedSignalConnection.lua +1 -1
  8. package/src/Shared/Members/Properties/TiePropertyDefinition.lua +58 -0
  9. package/src/Shared/{Interface → Members/Properties}/TiePropertyInterface.lua +42 -59
  10. package/src/Shared/Members/Signals/TieSignalConnection.lua +63 -0
  11. package/src/Shared/Members/Signals/TieSignalDefinition.lua +38 -0
  12. package/src/Shared/{Implementation → Members/Signals}/TieSignalImplementation.lua +29 -15
  13. package/src/Shared/Members/Signals/TieSignalInterface.lua +90 -0
  14. package/src/Shared/Members/TieMemberDefinition.lua +104 -0
  15. package/src/Shared/Members/TieMemberInterface.lua +94 -0
  16. package/src/Shared/Realms/TieRealmUtils.lua +41 -0
  17. package/src/Shared/{Definition/Types → Realms}/TieRealms.lua +3 -4
  18. package/src/Shared/Services/TieRealmService.lua +31 -0
  19. package/src/Shared/TieDefinition.lua +708 -0
  20. package/src/Shared/TieImplementation.lua +167 -0
  21. package/src/Shared/TieInterface.lua +129 -0
  22. package/src/Shared/{Encoding → Utils}/TieUtils.lua +4 -1
  23. package/test/modules/Server/Action/Action.lua +4 -7
  24. package/test/modules/Server/Door.lua +5 -10
  25. package/test/scripts/Server/ServerMain.server.lua +3 -3
  26. package/src/Shared/Definition/TieDefinition.lua +0 -507
  27. package/src/Shared/Definition/TieMethodDefinition.lua +0 -61
  28. package/src/Shared/Definition/TiePropertyDefinition.lua +0 -64
  29. package/src/Shared/Definition/TieSignalDefinition.lua +0 -59
  30. package/src/Shared/Definition/Types/TieRealmUtils.lua +0 -60
  31. package/src/Shared/Implementation/TieImplementation.lua +0 -129
  32. package/src/Shared/Interface/TieInterface.lua +0 -122
  33. package/src/Shared/Interface/TieInterfaceUtils.lua +0 -70
  34. package/src/Shared/Interface/TieMethodInterfaceUtils.lua +0 -43
  35. package/src/Shared/Interface/TieSignalConnection.lua +0 -89
  36. package/src/Shared/Interface/TieSignalInterface.lua +0 -61
  37. /package/src/Shared/{Implementation → Members/Properties}/TiePropertyImplementation.lua +0 -0
  38. /package/src/Shared/{Implementation → Members/Properties}/TiePropertyImplementationUtils.lua +0 -0
@@ -1,60 +0,0 @@
1
- --[=[
2
- @class TieRealmUtils
3
- ]=]
4
-
5
- local require = require(script.Parent.loader).load(script)
6
-
7
- local RunService = game:GetService("RunService")
8
- local TieRealms = require("TieRealms")
9
-
10
- local TieRealmUtils = {}
11
-
12
- function TieRealmUtils.isRequired(tieRealm)
13
- if tieRealm == TieRealms.CLIENT then
14
- -- Hack: TODO: Maybe we should contextualize the realm throughout all callers.
15
- -- But otherwise, we'll need to do this
16
- if not RunService:IsRunning() then
17
- return false
18
- end
19
-
20
- return RunService:IsClient()
21
- elseif tieRealm == TieRealms.SERVER then
22
- -- Hack: TODO: Maybe we should contextualize the realm throughout all callers.
23
- -- But otherwise, we'll need to do this
24
- if not RunService:IsRunning() then
25
- return false
26
- end
27
-
28
- return RunService:IsServer()
29
- elseif tieRealm == TieRealms.SHARED then
30
- return true
31
- else
32
- error("Unknown tieRealm")
33
- end
34
- end
35
-
36
- function TieRealmUtils.isAllowed(tieRealm)
37
- if tieRealm == TieRealms.CLIENT then
38
- -- Hack: TODO: Maybe we should contextualize the realm throughout all callers.
39
- -- But otherwise, we'll need to do this
40
- if not RunService:IsRunning() then
41
- return true
42
- end
43
-
44
- return RunService:IsClient()
45
- elseif tieRealm == TieRealms.SERVER then
46
- -- Hack: TODO: Maybe we should contextualize the realm throughout all callers.
47
- -- But otherwise, we'll need to do this
48
- if not RunService:IsRunning() then
49
- return true
50
- end
51
-
52
- return RunService:IsServer()
53
- elseif tieRealm == TieRealms.SHARED then
54
- return true
55
- else
56
- error("Unknown tieRealm")
57
- end
58
- end
59
-
60
- return TieRealmUtils
@@ -1,129 +0,0 @@
1
- --[=[
2
- This class represents the implementation for a given definition. For the lifetime
3
- of the class, this implementation will be exposed to consumption by both someone
4
- using the tie interface, and anyone invoking its methods via the normal Roblox API.
5
-
6
- @class TieImplementation
7
- ]=]
8
-
9
- local require = require(script.Parent.loader).load(script)
10
-
11
- local BaseObject = require("BaseObject")
12
- local TieRealms = require("TieRealms")
13
-
14
- local TieImplementation = setmetatable({}, BaseObject)
15
- TieImplementation.ClassName = "TieImplementation"
16
- TieImplementation.__index = TieImplementation
17
-
18
- function TieImplementation.new(tieDefinition, adornee, implementer)
19
- local self = setmetatable(BaseObject.new(), TieImplementation)
20
-
21
- self._definition = assert(tieDefinition, "No definition")
22
- self._adornee = assert(adornee, "No adornee")
23
- self._actualSelf = implementer or {}
24
-
25
- self._folder = self._maid:Add(Instance.new("Folder"))
26
- self._folder.Archivable = false
27
-
28
- self._memberImplementations = {}
29
- self._memberMap = self._definition:GetMemberMap()
30
-
31
- self:_buildMemberImplementations(implementer)
32
-
33
- self._folder.Parent = self._adornee
34
-
35
- return self
36
- end
37
-
38
- function TieImplementation:GetFolder()
39
- return self._folder
40
- end
41
-
42
- function TieImplementation:__index(index)
43
- if TieImplementation[index] then
44
- return TieImplementation[index]
45
- end
46
-
47
- if index == "_folder"
48
- or index == "_adornee"
49
- or index == "_definition"
50
- or index == "_memberImplementations"
51
- or index == "_memberMap"
52
- or index == "_actualSelf" then
53
-
54
- return rawget(self, index)
55
- end
56
-
57
- local memberMap = rawget(self, "_memberMap")
58
- local memberDefinition = memberMap[index]
59
- if memberDefinition then
60
- if memberDefinition:IsAllowed() then
61
- return memberDefinition:GetInterface(self._folder, self)
62
- else
63
- error(string.format("%q is not a valid member", tostring(index)))
64
- end
65
- else
66
- error(string.format("Bad index %q for TieImplementation", tostring(index)))
67
- end
68
- end
69
-
70
- function TieImplementation:__newindex(index, value)
71
- if index == "_folder"
72
- or index == "_adornee"
73
- or index == "_definition"
74
- or index == "_memberImplementations"
75
- or index == "_memberMap"
76
- or index == "_actualSelf" then
77
-
78
- rawset(self, index, value)
79
- elseif self._memberImplementations[index] then
80
- self._memberImplementations[index]:SetImplementation(value, self._actualSelf)
81
- elseif TieImplementation[index] then
82
- error(string.format("Cannot set %q in TieImplementation", tostring(index)))
83
- else
84
- error(string.format("Bad index %q for TieImplementation", tostring(index)))
85
- end
86
- end
87
-
88
- function TieImplementation:_buildMemberImplementations(implementer)
89
- local allClientImplemented = true
90
-
91
- for _, memberDefinition in pairs(self._memberMap) do
92
- if not memberDefinition:IsAllowed() then
93
- if memberDefinition:GetTieRealm() == TieRealms.CLIENT then
94
- allClientImplemented = false
95
- end
96
- continue
97
- end
98
-
99
- local initialValue
100
- if implementer then
101
- local memberName = memberDefinition:GetMemberName()
102
- initialValue = implementer[memberName]
103
- if not initialValue then
104
- if memberDefinition:IsRequired() then
105
- error(string.format("Missing member %q on %q", memberName, self._adornee:GetFullName()))
106
- else
107
- if memberDefinition:GetTieRealm() == TieRealms.CLIENT then
108
- allClientImplemented = false
109
- end
110
- continue
111
- end
112
- end
113
- else
114
- initialValue = nil
115
- end
116
-
117
- local memberImplementation = self._maid:Add(memberDefinition:Implement(self._folder, initialValue, self._actualSelf))
118
- self._memberImplementations[memberDefinition:GetMemberName()] = memberImplementation
119
- end
120
-
121
- -- Hack to make server objects look like server
122
- if allClientImplemented then
123
- self._folder.Name = self._definition:GetContainerName()
124
- else
125
- self._folder.Name = self._definition:GetContainerNameForServer()
126
- end
127
- end
128
-
129
- return TieImplementation
@@ -1,122 +0,0 @@
1
- --[=[
2
- @class TieInterface
3
- ]=]
4
-
5
- local require = require(script.Parent.loader).load(script)
6
-
7
- local TieSignalInterface = require("TieSignalInterface")
8
- local TiePropertyInterface = require("TiePropertyInterface")
9
- local TieMethodInterfaceUtils = require("TieMethodInterfaceUtils")
10
-
11
- local TieInterface = {}
12
- TieInterface.ClassName = "TieInterface"
13
- TieInterface.__index = TieInterface
14
-
15
- function TieInterface.new(definition, folder, adornee)
16
- local self = setmetatable({}, TieInterface)
17
-
18
- assert(folder or adornee, "Folder or adornee required")
19
-
20
- self._definition = assert(definition, "No definition")
21
- self._folder = folder -- could be nil
22
- self._adornee = adornee -- could be nil
23
- self._memberDefinitionMap = self._definition:GetMemberMap()
24
-
25
- return self
26
- end
27
-
28
- --[=[
29
- Returns whether this version of the definition is implemented to standard or not.
30
-
31
- @return boolean
32
- ]=]
33
- function TieInterface:IsImplemented()
34
- local folder = rawget(self, "_folder")
35
- local adornee = rawget(self, "_adornee")
36
- local definition = rawget(self, "_definition")
37
-
38
- if folder then
39
- if adornee then
40
- if folder.Parent ~= adornee then
41
- return false
42
- end
43
-
44
- if folder.Name ~= self:GetContainerName() then
45
- return false
46
- end
47
- end
48
-
49
- return definition:IsImplementation(folder)
50
- end
51
-
52
- return definition:HasImplementation(adornee)
53
- end
54
-
55
- --[=[
56
- Gets the adornee the tie interface is on if it can be found.
57
-
58
- @return Instance | nil
59
- ]=]
60
- function TieInterface:GetTieAdornee()
61
- local adornee = rawget(self, "_adornee")
62
- if adornee then
63
- return adornee
64
- end
65
-
66
- local folder = rawget(self, "_folder")
67
- if folder then
68
- return folder.Parent
69
- end
70
-
71
- return nil
72
- end
73
-
74
- --[=[
75
- @return Observable<boolean>
76
- ]=]
77
- function TieInterface:ObserveIsImplemented()
78
- local folder = rawget(self, "_folder")
79
- local adornee = rawget(self, "_adornee")
80
- local definition = rawget(self, "_definition")
81
-
82
- if folder then
83
- if adornee then
84
- return definition:ObserveIsImplementationOn(folder, adornee)
85
- else
86
- return definition:ObserveIsImplementation(folder)
87
- end
88
- end
89
-
90
- return definition:ObserveIsImplemented(adornee)
91
- end
92
-
93
- function TieInterface:__index(index)
94
- local member = rawget(self, "_memberDefinitionMap")[index]
95
- local definition = rawget(self, "_definition")
96
- if member and member:IsAllowed() then
97
- if member.ClassName == "TieMethodDefinition" then
98
- local adornee = rawget(self, "_adornee")
99
- local folder = rawget(self, "_folder")
100
-
101
- return TieMethodInterfaceUtils.get(self, definition, member, folder, adornee)
102
- elseif member.ClassName == "TieSignalDefinition" then
103
- local adornee = rawget(self, "_adornee")
104
- local folder = rawget(self, "_folder")
105
-
106
- return TieSignalInterface.new(folder, adornee, member)
107
- elseif member.ClassName == "TiePropertyDefinition" then
108
- local adornee = rawget(self, "_adornee")
109
- local folder = rawget(self, "_folder")
110
-
111
- return TiePropertyInterface.new(folder, adornee, member)
112
- else
113
- error(string.format("Unknown member definition %q", tostring(member.ClassName)))
114
- end
115
- elseif TieInterface[index] then
116
- return TieInterface[index]
117
- else
118
- error(string.format("Bad %q is not a member of %s", tostring(index), definition:GetContainerName()))
119
- end
120
- end
121
-
122
- return TieInterface
@@ -1,70 +0,0 @@
1
- --[=[
2
- @class TieInterfaceUtils
3
- ]=]
4
-
5
- local require = require(script.Parent.loader).load(script)
6
-
7
- local RxInstanceUtils = require("RxInstanceUtils")
8
- local Rx = require("Rx")
9
- local RxBrioUtils = require("RxBrioUtils")
10
-
11
- local TieInterfaceUtils = {}
12
-
13
- function TieInterfaceUtils.observeFolderBrio(tieDefinition, folder, adornee)
14
- if folder and adornee then
15
- local containerName = tieDefinition:GetContainerName()
16
- return Rx.combineLatest({
17
- Parent = RxInstanceUtils.observeProperty(folder, "Parent");
18
- Name = RxInstanceUtils.observeProperty(folder, "Name");
19
- })
20
- :Pipe({
21
- Rx.map(function(state)
22
- if state.Name == containerName and state.Parent == adornee then
23
- return folder
24
- else
25
- return nil
26
- end
27
- end);
28
- Rx.distinct();
29
- RxBrioUtils.toBrio();
30
- RxBrioUtils.onlyLastBrioSurvives();
31
- })
32
- elseif folder then
33
- local containerName = tieDefinition:GetContainerName()
34
- return RxInstanceUtils.observePropertyBrio(folder, "Name", function(name)
35
- return name == containerName
36
- end):Pipe({
37
- RxBrioUtils.map(function()
38
- return folder
39
- end);
40
- })
41
- elseif adornee then
42
- local containerName = tieDefinition:GetContainerName()
43
- return RxInstanceUtils.observeLastNamedChildBrio(adornee, "Folder", containerName)
44
- else
45
- error("No folder or adornee")
46
- end
47
- end
48
-
49
- function TieInterfaceUtils.getFolder(tieDefinition, folder, adornee)
50
- if folder and adornee then
51
- if folder.Parent == adornee and folder.Name == tieDefinition:GetContainerName() then
52
- return folder
53
- else
54
- return nil
55
- end
56
- elseif folder then
57
- if folder.Name == tieDefinition:GetContainerName() then
58
- return folder
59
- else
60
- return nil
61
- end
62
- elseif adornee then
63
- local folderName = tieDefinition:GetContainerName()
64
- return adornee:FindFirstChild(folderName)
65
- else
66
- error("Must have folder or adornee")
67
- end
68
- end
69
-
70
- return TieInterfaceUtils
@@ -1,43 +0,0 @@
1
- --[=[
2
- @class TieMethodInterfaceUtils
3
- ]=]
4
-
5
- local require = require(script.Parent.loader).load(script)
6
-
7
- local TieUtils = require("TieUtils")
8
-
9
- local TieMethodInterfaceUtils = {}
10
-
11
- function TieMethodInterfaceUtils.get(aliasSelf, definition, member, folder, adornee)
12
- return function(firstArg, ...)
13
- if firstArg ~= aliasSelf then
14
- error(("Must call methods with self as first parameter (Hint use `%s:%s()` instead of `%s.%s()`)"):format(
15
- definition:GetName(),
16
- member:GetMemberName(),
17
- definition:GetName(),
18
- member:GetMemberName()))
19
- end
20
-
21
- if folder and adornee then
22
- if folder.Parent ~= adornee then
23
- error("Folder is not a parent of the adornee")
24
- end
25
- elseif folder then
26
- folder = folder
27
- elseif adornee then
28
- folder = adornee:FindFirstChild(definition:GetContainerName())
29
- if not folder then
30
- error("No folder")
31
- end
32
- end
33
-
34
- local bindableFunction = folder:FindFirstChild(member:GetMemberName())
35
- if not bindableFunction then
36
- error(string.format("No bindableFunction implemented for %s on %q", member:GetMemberName(), folder:GetFullName()))
37
- end
38
-
39
- return TieUtils.decode(bindableFunction:Invoke(TieUtils.encode(...)))
40
- end;
41
- end
42
-
43
- return TieMethodInterfaceUtils
@@ -1,89 +0,0 @@
1
- --[=[
2
- @class TieSignalConnection
3
- ]=]
4
-
5
- local require = require(script.Parent.loader).load(script)
6
-
7
- local Maid = require("Maid")
8
- local TieInterfaceUtils = require("TieInterfaceUtils")
9
- local RxBrioUtils = require("RxBrioUtils")
10
- local RxInstanceUtils = require("RxInstanceUtils")
11
- local TieUtils = require("TieUtils")
12
-
13
- local TieSignalConnection = {}
14
- TieSignalConnection.ClassName = "TieSignalConnection"
15
- TieSignalConnection.__index = TieSignalConnection
16
-
17
- function TieSignalConnection.new(memberDefinition, folder, adornee, callback)
18
- local self = setmetatable({}, TieSignalConnection)
19
-
20
- assert(folder or adornee, "Folder or adornee required")
21
-
22
- self._maid = Maid.new()
23
-
24
- self._memberDefinition = assert(memberDefinition, "No memberDefinition")
25
- self._folder = folder
26
- self._adornee = adornee
27
- self._callback = assert(callback, "No callback")
28
-
29
- self._connected = true
30
- self._tieDefinition = self._memberDefinition:GetTieDefinition()
31
-
32
- self:_connect()
33
-
34
- return self
35
- end
36
-
37
- function TieSignalConnection:Disconnect()
38
- self:Destroy()
39
- end
40
-
41
- function TieSignalConnection:_connect()
42
- self._maid:GiveTask(self:_observeBindableEvent():Subscribe(function(brio)
43
- if brio:IsDead() then
44
- return
45
- end
46
-
47
- local maid = brio:ToMaid()
48
- local bindableEvent = brio:GetValue()
49
-
50
- maid:GiveTask(bindableEvent.Event:Connect(function(...)
51
- task.spawn(self._callback, TieUtils.decode(...))
52
- end))
53
- end))
54
- end
55
-
56
- function TieSignalConnection:_observeFolderBrio()
57
- return TieInterfaceUtils.observeFolderBrio(self._tieDefinition, self._folder, self._adornee)
58
- end
59
-
60
- function TieSignalConnection:_observeBindableEvent()
61
- local name = self._memberDefinition:GetMemberName()
62
-
63
- return self:_observeFolderBrio():Pipe({
64
- RxBrioUtils.switchMapBrio(function(folder)
65
- return RxInstanceUtils.observeLastNamedChildBrio(folder, "BindableEvent", name)
66
- end);
67
- RxBrioUtils.onlyLastBrioSurvives();
68
- })
69
- end
70
-
71
- function TieSignalConnection:__index(index)
72
- if index == "_adornee" or index == "_folder" then
73
- return rawget(self, index)
74
- elseif index == "IsConnected" then
75
- return self._connected
76
- elseif TieSignalConnection[index] then
77
- return TieSignalConnection[index]
78
- else
79
- error(("Bad index %q for TieSignalConnection"):format(tostring(index)))
80
- end
81
- end
82
-
83
- function TieSignalConnection:Destroy()
84
- self._connected = false
85
- self._maid:DoCleaning()
86
- -- Avoid setting the metatable so calling methods is always valid
87
- end
88
-
89
- return TieSignalConnection
@@ -1,61 +0,0 @@
1
- --[=[
2
- @class TieSignalInterface
3
- ]=]
4
-
5
- local require = require(script.Parent.loader).load(script)
6
-
7
- local TieSignalConnection = require("TieSignalConnection")
8
- local TieUtils = require("TieUtils")
9
- local TieInterfaceUtils = require("TieInterfaceUtils")
10
-
11
- local TieSignalInterface = {}
12
- TieSignalInterface.ClassName = "TieSignalInterface"
13
- TieSignalInterface.__index = TieSignalInterface
14
-
15
- function TieSignalInterface.new(folder, adornee, memberDefinition)
16
- local self = setmetatable({}, TieSignalInterface)
17
-
18
- assert(folder or adornee, "Folder or adornee required")
19
-
20
- self._folder = folder
21
- self._adornee = adornee
22
- self._memberDefinition = assert(memberDefinition, "No memberDefinition")
23
- self._tieDefinition = self._memberDefinition:GetTieDefinition()
24
-
25
- return self
26
- end
27
-
28
- function TieSignalInterface:Fire(...)
29
- local bindableEvent = self:_getBindableEvent()
30
- if not bindableEvent then
31
- warn(("[TieSignalInterface] - No bindableEvent for %q"):format(self._memberDefinition:GetMemberName()))
32
- end
33
-
34
- bindableEvent:Fire(TieUtils.encode(...))
35
- end
36
-
37
- function TieSignalInterface:Connect(callback)
38
- assert(type(callback) == "function", "Bad callback")
39
-
40
- return TieSignalConnection.new(self._memberDefinition, self._folder, self._adornee, callback)
41
- end
42
-
43
- function TieSignalInterface:_getBindableEvent()
44
- local folder = self:_getFolder()
45
- if not folder then
46
- return nil
47
- end
48
-
49
- local implementation = folder:FindFirstChild(self._memberDefinition:GetMemberName())
50
- if implementation and implementation:IsA("BindableEvent") then
51
- return implementation
52
- else
53
- return nil
54
- end
55
- end
56
-
57
- function TieSignalInterface:_getFolder()
58
- return TieInterfaceUtils.getFolder(self._tieDefinition, self._folder, self._adornee)
59
- end
60
-
61
- return TieSignalInterface