dominus-cli 2.0.0 → 2.1.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.
@@ -1,30 +1,30 @@
1
- --[[
2
- Message protocol: JSON encoding/decoding with unique ID generation
3
- ]]
4
-
5
- local HttpService = game:GetService("HttpService")
6
-
7
- local Protocol = {}
8
-
9
- function Protocol.encode(data)
10
- return HttpService:JSONEncode(data)
11
- end
12
-
13
- function Protocol.decode(json)
14
- return HttpService:JSONDecode(json)
15
- end
16
-
17
- function Protocol.generateId()
18
- return HttpService:GenerateGUID(false)
19
- end
20
-
21
- function Protocol.createMessage(msgType, payload)
22
- return {
23
- id = Protocol.generateId(),
24
- type = msgType,
25
- payload = payload,
26
- ts = os.clock(),
27
- }
28
- end
29
-
30
- return Protocol
1
+ --[[
2
+ Message protocol: JSON encoding/decoding with unique ID generation
3
+ ]]
4
+
5
+ local HttpService = game:GetService("HttpService")
6
+
7
+ local Protocol = {}
8
+
9
+ function Protocol.encode(data)
10
+ return HttpService:JSONEncode(data)
11
+ end
12
+
13
+ function Protocol.decode(json)
14
+ return HttpService:JSONDecode(json)
15
+ end
16
+
17
+ function Protocol.generateId()
18
+ return HttpService:GenerateGUID(false)
19
+ end
20
+
21
+ function Protocol.createMessage(msgType, payload)
22
+ return {
23
+ id = Protocol.generateId(),
24
+ type = msgType,
25
+ payload = payload,
26
+ ts = os.clock(),
27
+ }
28
+ end
29
+
30
+ return Protocol
@@ -1,22 +1,39 @@
1
- --[[
2
- Reflection module: query Roblox class/property/method/event information
3
- Uses the new ReflectionService API (2025+):
4
- - GetClass(className, filter?)
5
- - GetClasses(filter?)
6
- - GetPropertiesOfClass(className, filter?)
7
- - GetMethodsOfClass(className, filter?)
8
- - GetEventsOfClass(className, filter?)
9
- ]]
10
-
11
1
  local ReflectionService = game:GetService("ReflectionService")
2
+
12
3
  local Reflection = {}
13
4
 
14
- local function safeCall(fn, ...)
15
- local ok, result = pcall(fn, ...)
16
- if ok then return result end
5
+ local function safeCall(callback)
6
+ local ok, result = pcall(callback)
7
+ if ok then
8
+ return result
9
+ end
17
10
  return nil, result
18
11
  end
19
12
 
13
+ local function typeName(reflectionType)
14
+ if reflectionType == nil then
15
+ return "unknown"
16
+ end
17
+ if type(reflectionType) == "table" then
18
+ return reflectionType.Name or reflectionType.name or reflectionType.Type or tostring(reflectionType)
19
+ end
20
+ return tostring(reflectionType)
21
+ end
22
+
23
+ local function displayCategory(member)
24
+ if member.Display and member.Display.Category then
25
+ return member.Display.Category
26
+ end
27
+ return "Other"
28
+ end
29
+
30
+ local function permitString(permits, key)
31
+ if not permits or permits[key] == nil then
32
+ return nil
33
+ end
34
+ return tostring(permits[key])
35
+ end
36
+
20
37
  function Reflection.getClassInfo(className)
21
38
  local classData, classErr = safeCall(function()
22
39
  return ReflectionService:GetClass(className)
@@ -27,13 +44,15 @@ function Reflection.getClassInfo(className)
27
44
  return ReflectionService:GetPropertiesOfClass(className)
28
45
  end)
29
46
  if propData then
30
- for _, prop in propData do
47
+ for _, property in propData do
31
48
  table.insert(properties, {
32
- name = prop.Name,
33
- type = prop.ValueType and tostring(prop.ValueType) or "unknown",
34
- category = prop.Category or "Other",
35
- serialized = prop.Serialized,
36
- owner = prop.Owner,
49
+ name = property.Name,
50
+ type = typeName(property.Type),
51
+ category = displayCategory(property),
52
+ owner = property.Owner,
53
+ serialized = property.Serialized,
54
+ readPermission = permitString(property.Permits, "Read"),
55
+ writePermission = permitString(property.Permits, "Write"),
37
56
  })
38
57
  end
39
58
  end
@@ -44,18 +63,19 @@ function Reflection.getClassInfo(className)
44
63
  end)
45
64
  if methodData then
46
65
  for _, method in methodData do
47
- local params = {}
48
- if method.Parameters then
49
- for _, param in method.Parameters do
50
- table.insert(params, {
51
- name = param.Name,
52
- type = param.Type and tostring(param.Type) or "unknown",
53
- })
54
- end
66
+ local parameters = {}
67
+ for _, parameter in method.Parameters or {} do
68
+ table.insert(parameters, {
69
+ name = parameter.Name,
70
+ type = typeName(parameter.Type),
71
+ defaultValue = parameter.DefaultValue,
72
+ })
55
73
  end
56
74
  table.insert(methods, {
57
75
  name = method.Name,
58
- parameters = params,
76
+ parameters = parameters,
77
+ returnType = typeName(method.ReturnType),
78
+ canYield = method.CanYield,
59
79
  owner = method.Owner,
60
80
  })
61
81
  end
@@ -67,70 +87,82 @@ function Reflection.getClassInfo(className)
67
87
  end)
68
88
  if eventData then
69
89
  for _, event in eventData do
70
- local params = {}
71
- if event.Parameters then
72
- for _, param in event.Parameters do
73
- table.insert(params, {
74
- name = param.Name,
75
- type = param.Type and tostring(param.Type) or "unknown",
76
- })
77
- end
90
+ local parameters = {}
91
+ for _, parameter in event.Parameters or {} do
92
+ table.insert(parameters, { name = parameter.Name, type = typeName(parameter.Type) })
78
93
  end
79
- table.insert(events, {
80
- name = event.Name,
81
- parameters = params,
82
- owner = event.Owner,
83
- })
94
+ table.insert(events, { name = event.Name, parameters = parameters, owner = event.Owner })
84
95
  end
85
96
  end
86
97
 
87
98
  local result = {
99
+ success = classData ~= nil or #properties > 0,
88
100
  className = className,
89
101
  properties = properties,
90
102
  methods = methods,
91
103
  events = events,
92
104
  }
93
-
94
105
  if classData then
95
- result.superclass = classData.Superclass
96
- result.creatable = classData.Creatable
97
- result.serialized = classData.Serialized
106
+ result.superclass = classData.Superclass and tostring(classData.Superclass) or nil
107
+ result.newPermission = permitString(classData.Permits, "New")
108
+ result.creatable = classData.Permits ~= nil and classData.Permits.New ~= nil
98
109
  end
99
-
100
110
  if classErr and #properties == 0 then
101
111
  result.error = "Could not reflect class: " .. tostring(classErr)
102
112
  end
103
-
104
113
  return result
105
114
  end
106
115
 
107
116
  function Reflection.getPropertyTypes(className)
108
- local propData = safeCall(function()
117
+ local properties = safeCall(function()
109
118
  return ReflectionService:GetPropertiesOfClass(className)
110
119
  end)
111
- if not propData then return {} end
120
+ if not properties then
121
+ return {}
122
+ end
123
+ local result = {}
124
+ for _, property in properties do
125
+ result[property.Name] = typeName(property.Type)
126
+ end
127
+ return result
128
+ end
112
129
 
113
- local typeMap = {}
114
- for _, prop in propData do
115
- typeMap[prop.Name] = prop.ValueType and tostring(prop.ValueType) or "unknown"
130
+ function Reflection.getPropertyMetadata(className)
131
+ local properties = safeCall(function()
132
+ return ReflectionService:GetPropertiesOfClass(className)
133
+ end)
134
+ if not properties then
135
+ return {}
136
+ end
137
+ local result = {}
138
+ for _, property in properties do
139
+ result[property.Name] = {
140
+ type = typeName(property.Type),
141
+ category = displayCategory(property),
142
+ writable = property.Permits == nil or property.Permits.Write ~= nil,
143
+ }
116
144
  end
117
- return typeMap
145
+ return result
118
146
  end
119
147
 
120
148
  function Reflection.listClasses()
121
149
  local classes = safeCall(function()
122
150
  return ReflectionService:GetClasses()
123
151
  end)
124
- if not classes then return {} end
125
-
152
+ if not classes then
153
+ return {}
154
+ end
126
155
  local result = {}
127
- for _, cls in classes do
156
+ for _, classData in classes do
128
157
  table.insert(result, {
129
- name = cls.Name,
130
- superclass = cls.Superclass,
131
- creatable = cls.Creatable,
158
+ name = classData.Name,
159
+ superclass = classData.Superclass and tostring(classData.Superclass) or nil,
160
+ creatable = classData.Permits ~= nil and classData.Permits.New ~= nil,
132
161
  })
133
162
  end
163
+ table.sort(result, function(a, b)
164
+ return a.name < b.name
165
+ end)
134
166
  return result
135
167
  end
136
168
 
@@ -1,141 +1,69 @@
1
- --[[
2
- TestRunner: interface with StudioTestService for automated testing
3
- https://create.roblox.com/docs/reference/engine/classes/StudioTestService
4
-
5
- StudioTestService allows plugins to:
6
- - ExecuteRunModeAsync(args) → start Run mode, yield until EndTest
7
- - ExecutePlayModeAsync(args) → start Play/Solo mode, yield until EndTest
8
- - GetTestArgs() retrieve args from inside the running session
9
- - EndTest(value) → end the session and return a value
10
- ]]
11
-
12
- local TestRunner = {}
13
-
14
- local StudioTestService = game:GetService("StudioTestService")
15
-
16
- function TestRunner.executeRunMode(args)
17
- local startTime = os.clock()
18
- local ok, result = pcall(function()
19
- return StudioTestService:ExecuteRunModeAsync(args)
20
- end)
21
- local duration = os.clock() - startTime
22
-
23
- if not ok then
24
- return {
25
- success = false,
26
- error = tostring(result),
27
- duration = duration,
28
- }
29
- end
30
-
31
- return {
32
- success = true,
33
- result = result,
34
- duration = duration,
35
- }
36
- end
37
-
38
- function TestRunner.executePlayMode(args)
39
- local startTime = os.clock()
40
- local ok, result = pcall(function()
41
- return StudioTestService:ExecutePlayModeAsync(args)
42
- end)
43
- local duration = os.clock() - startTime
44
-
45
- if not ok then
46
- return {
47
- success = false,
48
- error = tostring(result),
49
- duration = duration,
50
- }
51
- end
52
-
53
- return {
54
- success = true,
55
- result = result,
56
- duration = duration,
57
- }
58
- end
59
-
60
- function TestRunner.endTest(value)
61
- local ok, err = pcall(function()
62
- StudioTestService:EndTest(value)
63
- end)
64
-
65
- if not ok then
66
- return { success = false, error = tostring(err) }
67
- end
68
-
69
- return { success = true }
70
- end
71
-
72
- function TestRunner.run(testName)
73
- local results = {}
74
-
75
- local ok, err = pcall(function()
76
- local TestService = game:GetService("TestService")
77
-
78
- if testName then
79
- local testScript = nil
80
- for _, desc in game:GetDescendants() do
81
- if desc:IsA("ModuleScript") and desc.Name == testName then
82
- testScript = desc
83
- break
84
- end
85
- end
86
-
87
- if not testScript then
88
- table.insert(results, {
89
- name = testName,
90
- passed = false,
91
- duration = 0,
92
- error = "Test not found: " .. testName,
93
- })
94
- return
95
- end
96
-
97
- local startTime = os.clock()
98
- local testOk, testErr = pcall(function()
99
- require(testScript)
100
- end)
101
- local duration = os.clock() - startTime
102
-
103
- table.insert(results, {
104
- name = testName,
105
- passed = testOk,
106
- duration = duration,
107
- error = not testOk and tostring(testErr) or nil,
108
- })
109
- else
110
- for _, desc in game:GetDescendants() do
111
- if desc:IsA("ModuleScript") then
112
- local name = desc.Name
113
- if string.match(name, "%.test$") or string.match(name, "%.spec$")
114
- or string.match(name, "Test$") or string.match(name, "Spec$") then
115
-
116
- local startTime = os.clock()
117
- local testOk, testErr = pcall(function()
118
- require(desc)
119
- end)
120
- local duration = os.clock() - startTime
121
-
122
- table.insert(results, {
123
- name = name,
124
- passed = testOk,
125
- duration = duration,
126
- error = not testOk and tostring(testErr) or nil,
127
- })
128
- end
129
- end
130
- end
131
- end
132
- end)
133
-
134
- if not ok then
135
- return { results = {{ name = "TestRunner", passed = false, duration = 0, error = tostring(err) }} }
136
- end
137
-
138
- return { results = results }
139
- end
140
-
141
- return TestRunner
1
+ local StudioTestService = game:GetService("StudioTestService")
2
+
3
+ local TestRunner = {}
4
+ local activeRunId = nil
5
+
6
+ function TestRunner.executeV2(spec)
7
+ if activeRunId then
8
+ return { success = false, error = "A Dominus Studio test is already running" }
9
+ end
10
+
11
+ local mode = spec.mode or "run"
12
+ if mode ~= "run" and mode ~= "play" and mode ~= "multiplayer" then
13
+ return { success = false, error = "mode must be run, play, or multiplayer" }
14
+ end
15
+ local timeoutSeconds = math.clamp((spec.timeoutMs or 120000) / 1000, 5, 600)
16
+ local players = math.clamp(spec.players or 1, 1, 8)
17
+ local runId = tostring(os.clock()) .. ":" .. mode
18
+ activeRunId = runId
19
+ local timedOut = false
20
+
21
+ task.delay(timeoutSeconds, function()
22
+ if activeRunId ~= runId then
23
+ return
24
+ end
25
+ timedOut = true
26
+ pcall(function()
27
+ if StudioTestService:CanLeaveTest() then
28
+ StudioTestService:LeaveTest()
29
+ end
30
+ end)
31
+ end)
32
+
33
+ local startedAt = os.clock()
34
+ local ok, result = pcall(function()
35
+ if mode == "run" then
36
+ return StudioTestService:ExecuteRunModeAsync(spec.args)
37
+ elseif mode == "play" then
38
+ return StudioTestService:ExecutePlayModeAsync(spec.args)
39
+ end
40
+ return StudioTestService:ExecuteMultiplayerTestAsync(players, spec.args)
41
+ end)
42
+ local duration = os.clock() - startedAt
43
+ if activeRunId == runId then
44
+ activeRunId = nil
45
+ end
46
+
47
+ if timedOut then
48
+ return { success = false, timedOut = true, error = "Studio test exceeded its timeout", duration = duration }
49
+ end
50
+ if not ok then
51
+ return { success = false, error = tostring(result), duration = duration }
52
+ end
53
+ return {
54
+ success = true,
55
+ mode = mode,
56
+ players = mode == "multiplayer" and players or nil,
57
+ result = result,
58
+ duration = duration,
59
+ }
60
+ end
61
+
62
+ function TestRunner.endTest(value)
63
+ local ok, err = pcall(function()
64
+ StudioTestService:EndTest(value)
65
+ end)
66
+ return ok and { success = true } or { success = false, error = tostring(err) }
67
+ end
68
+
69
+ return TestRunner