roblox-opencode 1.0.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 (248) hide show
  1. package/README.md +122 -0
  2. package/commands/setup-game.md +108 -0
  3. package/commands/sync-check.md +53 -0
  4. package/core/roblox-core.md +93 -0
  5. package/dist/server.js +167 -0
  6. package/package.json +35 -0
  7. package/skills/roblox-analytics/SKILL.md +277 -0
  8. package/skills/roblox-analytics/references/event-batcher.luau +75 -0
  9. package/skills/roblox-animation-vfx/SKILL.md +1325 -0
  10. package/skills/roblox-architecture/SKILL.md +863 -0
  11. package/skills/roblox-architecture/references/combat-systems.md +1381 -0
  12. package/skills/roblox-code-review/SKILL.md +687 -0
  13. package/skills/roblox-data/SKILL.md +889 -0
  14. package/skills/roblox-data/references/inventory-systems.md +1729 -0
  15. package/skills/roblox-debug/SKILL.md +99 -0
  16. package/skills/roblox-gui/SKILL.md +1103 -0
  17. package/skills/roblox-gui-fusion/SKILL.md +150 -0
  18. package/skills/roblox-gui-fusion/references/inventory.luau +427 -0
  19. package/skills/roblox-gui-fusion/references/settings-menu.luau +579 -0
  20. package/skills/roblox-gui-fusion/references/shop.luau +411 -0
  21. package/skills/roblox-luau-mastery/SKILL.md +1519 -0
  22. package/skills/roblox-monetization/SKILL.md +1084 -0
  23. package/skills/roblox-monetization/references/process-receipt.luau +131 -0
  24. package/skills/roblox-networking/SKILL.md +669 -0
  25. package/skills/roblox-networking/references/remote-validator.luau +193 -0
  26. package/skills/roblox-publish-checklist/SKILL.md +128 -0
  27. package/skills/roblox-runtime/SKILL.md +753 -0
  28. package/skills/roblox-sharp-edges/SKILL.md +295 -0
  29. package/skills/roblox-sync/SKILL.md +126 -0
  30. package/skills/roblox-testing/SKILL.md +943 -0
  31. package/skills/roblox-tooling/SKILL.md +150 -0
  32. package/vendor/LICENSES/ProfileStore-LICENSE +201 -0
  33. package/vendor/LICENSES/RbxUtil-LICENSE +7 -0
  34. package/vendor/LICENSES/promise-LICENSE +21 -0
  35. package/vendor/LICENSES/t-LICENSE +21 -0
  36. package/vendor/LICENSES/testez-LICENSE +201 -0
  37. package/vendor/README.md +84 -0
  38. package/vendor/fusion/Animation/ExternalTime.luau +84 -0
  39. package/vendor/fusion/Animation/Spring.luau +322 -0
  40. package/vendor/fusion/Animation/Stopwatch.luau +128 -0
  41. package/vendor/fusion/Animation/Tween.luau +187 -0
  42. package/vendor/fusion/Animation/getTweenDuration.luau +27 -0
  43. package/vendor/fusion/Animation/getTweenRatio.luau +47 -0
  44. package/vendor/fusion/Animation/lerpType.luau +164 -0
  45. package/vendor/fusion/Animation/packType.luau +100 -0
  46. package/vendor/fusion/Animation/springCoefficients.luau +80 -0
  47. package/vendor/fusion/Animation/unpackType.luau +103 -0
  48. package/vendor/fusion/Colour/Oklab.luau +70 -0
  49. package/vendor/fusion/Colour/sRGB.luau +55 -0
  50. package/vendor/fusion/External.luau +168 -0
  51. package/vendor/fusion/ExternalDebug.luau +70 -0
  52. package/vendor/fusion/Graph/Observer.luau +114 -0
  53. package/vendor/fusion/Graph/castToGraph.luau +29 -0
  54. package/vendor/fusion/Graph/change.luau +81 -0
  55. package/vendor/fusion/Graph/depend.luau +33 -0
  56. package/vendor/fusion/Graph/evaluate.luau +56 -0
  57. package/vendor/fusion/Instances/Attribute.luau +58 -0
  58. package/vendor/fusion/Instances/AttributeChange.luau +47 -0
  59. package/vendor/fusion/Instances/AttributeOut.luau +63 -0
  60. package/vendor/fusion/Instances/Child.luau +21 -0
  61. package/vendor/fusion/Instances/Children.luau +148 -0
  62. package/vendor/fusion/Instances/Hydrate.luau +33 -0
  63. package/vendor/fusion/Instances/New.luau +53 -0
  64. package/vendor/fusion/Instances/OnChange.luau +50 -0
  65. package/vendor/fusion/Instances/OnEvent.luau +54 -0
  66. package/vendor/fusion/Instances/Out.luau +69 -0
  67. package/vendor/fusion/Instances/applyInstanceProps.luau +149 -0
  68. package/vendor/fusion/Instances/defaultProps.luau +194 -0
  69. package/vendor/fusion/LICENSE +21 -0
  70. package/vendor/fusion/Logging/formatError.luau +49 -0
  71. package/vendor/fusion/Logging/messages.luau +52 -0
  72. package/vendor/fusion/Logging/parseError.luau +25 -0
  73. package/vendor/fusion/Memory/checkLifetime.luau +134 -0
  74. package/vendor/fusion/Memory/deriveScope.luau +24 -0
  75. package/vendor/fusion/Memory/deriveScopeImpl.luau +45 -0
  76. package/vendor/fusion/Memory/doCleanup.luau +79 -0
  77. package/vendor/fusion/Memory/innerScope.luau +34 -0
  78. package/vendor/fusion/Memory/legacyCleanup.luau +18 -0
  79. package/vendor/fusion/Memory/needsDestruction.luau +17 -0
  80. package/vendor/fusion/Memory/poisonScope.luau +34 -0
  81. package/vendor/fusion/Memory/scopePool.luau +55 -0
  82. package/vendor/fusion/Memory/scoped.luau +27 -0
  83. package/vendor/fusion/Memory/whichLivesLonger.luau +75 -0
  84. package/vendor/fusion/RobloxExternal.luau +98 -0
  85. package/vendor/fusion/State/Computed.luau +139 -0
  86. package/vendor/fusion/State/For/Disassembly.luau +211 -0
  87. package/vendor/fusion/State/For/ForTypes.luau +30 -0
  88. package/vendor/fusion/State/For/init.luau +110 -0
  89. package/vendor/fusion/State/ForKeys.luau +94 -0
  90. package/vendor/fusion/State/ForPairs.luau +97 -0
  91. package/vendor/fusion/State/ForValues.luau +94 -0
  92. package/vendor/fusion/State/Value.luau +88 -0
  93. package/vendor/fusion/State/castToState.luau +26 -0
  94. package/vendor/fusion/State/peek.luau +31 -0
  95. package/vendor/fusion/State/updateAll.luau +1 -0
  96. package/vendor/fusion/Types.luau +314 -0
  97. package/vendor/fusion/Utility/Contextual.luau +91 -0
  98. package/vendor/fusion/Utility/Safe.luau +23 -0
  99. package/vendor/fusion/Utility/isSimilar.luau +29 -0
  100. package/vendor/fusion/Utility/merge.luau +35 -0
  101. package/vendor/fusion/Utility/nameOf.luau +35 -0
  102. package/vendor/fusion/Utility/never.luau +14 -0
  103. package/vendor/fusion/Utility/nicknames.luau +11 -0
  104. package/vendor/fusion/Utility/xtypeof.luau +27 -0
  105. package/vendor/fusion/init.luau +82 -0
  106. package/vendor/profilestore/init.luau +2243 -0
  107. package/vendor/promise/init.luau +1982 -0
  108. package/vendor/rbxutil/buffer-util/Buffer.test.luau +25 -0
  109. package/vendor/rbxutil/buffer-util/BufferReader.luau +228 -0
  110. package/vendor/rbxutil/buffer-util/BufferWriter.luau +269 -0
  111. package/vendor/rbxutil/buffer-util/DataTypeBuffer.luau +223 -0
  112. package/vendor/rbxutil/buffer-util/Types.luau +60 -0
  113. package/vendor/rbxutil/buffer-util/index.d.ts +153 -0
  114. package/vendor/rbxutil/buffer-util/init.luau +41 -0
  115. package/vendor/rbxutil/buffer-util/package.json +16 -0
  116. package/vendor/rbxutil/buffer-util/wally.toml +9 -0
  117. package/vendor/rbxutil/comm/Client/ClientComm.luau +232 -0
  118. package/vendor/rbxutil/comm/Client/ClientRemoteProperty.luau +156 -0
  119. package/vendor/rbxutil/comm/Client/ClientRemoteSignal.luau +109 -0
  120. package/vendor/rbxutil/comm/Client/init.luau +135 -0
  121. package/vendor/rbxutil/comm/Server/RemoteProperty.luau +295 -0
  122. package/vendor/rbxutil/comm/Server/RemoteSignal.luau +211 -0
  123. package/vendor/rbxutil/comm/Server/ServerComm.luau +211 -0
  124. package/vendor/rbxutil/comm/Server/init.luau +140 -0
  125. package/vendor/rbxutil/comm/Types.luau +18 -0
  126. package/vendor/rbxutil/comm/Util.luau +27 -0
  127. package/vendor/rbxutil/comm/init.luau +35 -0
  128. package/vendor/rbxutil/comm/wally.toml +13 -0
  129. package/vendor/rbxutil/component/init.luau +759 -0
  130. package/vendor/rbxutil/component/init.test.luau +311 -0
  131. package/vendor/rbxutil/component/wally.toml +14 -0
  132. package/vendor/rbxutil/concur/init.luau +542 -0
  133. package/vendor/rbxutil/concur/init.test.luau +364 -0
  134. package/vendor/rbxutil/concur/wally.toml +8 -0
  135. package/vendor/rbxutil/enum-list/init.luau +101 -0
  136. package/vendor/rbxutil/enum-list/init.test.luau +91 -0
  137. package/vendor/rbxutil/enum-list/wally.toml +8 -0
  138. package/vendor/rbxutil/find/index.d.ts +20 -0
  139. package/vendor/rbxutil/find/init.luau +44 -0
  140. package/vendor/rbxutil/find/package.json +17 -0
  141. package/vendor/rbxutil/find/wally.toml +8 -0
  142. package/vendor/rbxutil/input/Gamepad.luau +559 -0
  143. package/vendor/rbxutil/input/Keyboard.luau +124 -0
  144. package/vendor/rbxutil/input/Mouse.luau +278 -0
  145. package/vendor/rbxutil/input/PreferredInput.luau +91 -0
  146. package/vendor/rbxutil/input/Touch.luau +120 -0
  147. package/vendor/rbxutil/input/init.luau +33 -0
  148. package/vendor/rbxutil/input/wally.toml +12 -0
  149. package/vendor/rbxutil/loader/index.d.ts +15 -0
  150. package/vendor/rbxutil/loader/init.luau +137 -0
  151. package/vendor/rbxutil/loader/wally.toml +8 -0
  152. package/vendor/rbxutil/log/index.d.ts +38 -0
  153. package/vendor/rbxutil/log/init.luau +746 -0
  154. package/vendor/rbxutil/log/wally.toml +8 -0
  155. package/vendor/rbxutil/net/init.luau +190 -0
  156. package/vendor/rbxutil/net/wally.toml +8 -0
  157. package/vendor/rbxutil/option/index.d.ts +44 -0
  158. package/vendor/rbxutil/option/init.luau +489 -0
  159. package/vendor/rbxutil/option/init.test.luau +342 -0
  160. package/vendor/rbxutil/option/wally.toml +8 -0
  161. package/vendor/rbxutil/pid/index.d.ts +53 -0
  162. package/vendor/rbxutil/pid/init.luau +195 -0
  163. package/vendor/rbxutil/pid/package.json +16 -0
  164. package/vendor/rbxutil/pid/wally.toml +9 -0
  165. package/vendor/rbxutil/quaternion/index.d.ts +117 -0
  166. package/vendor/rbxutil/quaternion/init.luau +570 -0
  167. package/vendor/rbxutil/quaternion/package.json +16 -0
  168. package/vendor/rbxutil/quaternion/wally.toml +9 -0
  169. package/vendor/rbxutil/query/index.d.ts +43 -0
  170. package/vendor/rbxutil/query/init.luau +117 -0
  171. package/vendor/rbxutil/query/package.json +18 -0
  172. package/vendor/rbxutil/query/wally.toml +9 -0
  173. package/vendor/rbxutil/sequent/index.d.ts +28 -0
  174. package/vendor/rbxutil/sequent/init.luau +340 -0
  175. package/vendor/rbxutil/sequent/package.json +16 -0
  176. package/vendor/rbxutil/sequent/wally.toml +9 -0
  177. package/vendor/rbxutil/ser/init.luau +175 -0
  178. package/vendor/rbxutil/ser/init.test.luau +50 -0
  179. package/vendor/rbxutil/ser/wally.toml +11 -0
  180. package/vendor/rbxutil/shake/index.d.ts +36 -0
  181. package/vendor/rbxutil/shake/init.luau +532 -0
  182. package/vendor/rbxutil/shake/init.test.luau +267 -0
  183. package/vendor/rbxutil/shake/package.json +16 -0
  184. package/vendor/rbxutil/shake/wally.toml +9 -0
  185. package/vendor/rbxutil/signal/index.d.ts +100 -0
  186. package/vendor/rbxutil/signal/init.luau +432 -0
  187. package/vendor/rbxutil/signal/init.test.luau +190 -0
  188. package/vendor/rbxutil/signal/package.json +17 -0
  189. package/vendor/rbxutil/signal/wally.toml +9 -0
  190. package/vendor/rbxutil/silo/TableWatcher.luau +65 -0
  191. package/vendor/rbxutil/silo/Util.luau +55 -0
  192. package/vendor/rbxutil/silo/init.luau +338 -0
  193. package/vendor/rbxutil/silo/init.test.luau +215 -0
  194. package/vendor/rbxutil/silo/wally.toml +8 -0
  195. package/vendor/rbxutil/spring/index.d.ts +40 -0
  196. package/vendor/rbxutil/spring/init.luau +97 -0
  197. package/vendor/rbxutil/spring/package.json +17 -0
  198. package/vendor/rbxutil/spring/wally.toml +8 -0
  199. package/vendor/rbxutil/stream/index.d.ts +88 -0
  200. package/vendor/rbxutil/stream/init.luau +597 -0
  201. package/vendor/rbxutil/stream/package.json +18 -0
  202. package/vendor/rbxutil/stream/wally.toml +9 -0
  203. package/vendor/rbxutil/streamable/Streamable.luau +202 -0
  204. package/vendor/rbxutil/streamable/StreamableUtil.luau +80 -0
  205. package/vendor/rbxutil/streamable/init.luau +8 -0
  206. package/vendor/rbxutil/streamable/wally.toml +12 -0
  207. package/vendor/rbxutil/symbol/init.luau +56 -0
  208. package/vendor/rbxutil/symbol/init.test.luau +37 -0
  209. package/vendor/rbxutil/symbol/wally.toml +8 -0
  210. package/vendor/rbxutil/table-util/init.luau +938 -0
  211. package/vendor/rbxutil/table-util/init.test.luau +439 -0
  212. package/vendor/rbxutil/table-util/wally.toml +8 -0
  213. package/vendor/rbxutil/task-queue/index.d.ts +27 -0
  214. package/vendor/rbxutil/task-queue/init.luau +97 -0
  215. package/vendor/rbxutil/task-queue/wally.toml +8 -0
  216. package/vendor/rbxutil/timer/index.d.ts +81 -0
  217. package/vendor/rbxutil/timer/init.luau +249 -0
  218. package/vendor/rbxutil/timer/init.test.luau +73 -0
  219. package/vendor/rbxutil/timer/wally.toml +11 -0
  220. package/vendor/rbxutil/tree/index.d.ts +15 -0
  221. package/vendor/rbxutil/tree/init.luau +137 -0
  222. package/vendor/rbxutil/tree/wally.toml +8 -0
  223. package/vendor/rbxutil/trove/index.d.ts +46 -0
  224. package/vendor/rbxutil/trove/init.luau +787 -0
  225. package/vendor/rbxutil/trove/init.test.luau +203 -0
  226. package/vendor/rbxutil/trove/wally.toml +8 -0
  227. package/vendor/rbxutil/typed-remote/init.luau +196 -0
  228. package/vendor/rbxutil/typed-remote/wally.toml +8 -0
  229. package/vendor/rbxutil/wait-for/index.d.ts +17 -0
  230. package/vendor/rbxutil/wait-for/init.luau +257 -0
  231. package/vendor/rbxutil/wait-for/init.test.luau +182 -0
  232. package/vendor/rbxutil/wait-for/wally.toml +11 -0
  233. package/vendor/t/t.lua +1350 -0
  234. package/vendor/testez/Context.lua +26 -0
  235. package/vendor/testez/Expectation.lua +311 -0
  236. package/vendor/testez/ExpectationContext.lua +38 -0
  237. package/vendor/testez/LifecycleHooks.lua +89 -0
  238. package/vendor/testez/Reporters/TeamCityReporter.lua +102 -0
  239. package/vendor/testez/Reporters/TextReporter.lua +106 -0
  240. package/vendor/testez/Reporters/TextReporterQuiet.lua +97 -0
  241. package/vendor/testez/TestBootstrap.lua +147 -0
  242. package/vendor/testez/TestEnum.lua +28 -0
  243. package/vendor/testez/TestPlan.lua +304 -0
  244. package/vendor/testez/TestPlanner.lua +40 -0
  245. package/vendor/testez/TestResults.lua +112 -0
  246. package/vendor/testez/TestRunner.lua +188 -0
  247. package/vendor/testez/TestSession.lua +243 -0
  248. package/vendor/testez/init.lua +40 -0
@@ -0,0 +1,58 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ --[[
7
+ A special key for property tables, which allows users to apply custom
8
+ attributes to instances
9
+ ]]
10
+
11
+ local Package = script.Parent.Parent
12
+ local Types = require(Package.Types)
13
+ -- Memory
14
+ local checkLifetime = require(Package.Memory.checkLifetime)
15
+ -- Graph
16
+ local Observer = require(Package.Graph.Observer)
17
+ -- State
18
+ local castToState = require(Package.State.castToState)
19
+ local peek = require(Package.State.peek)
20
+
21
+ local keyCache: {[string]: Types.SpecialKey} = {}
22
+
23
+ local function Attribute(
24
+ attributeName: string
25
+ ): Types.SpecialKey
26
+ local key = keyCache[attributeName]
27
+ if key == nil then
28
+ key = {
29
+ type = "SpecialKey",
30
+ kind = "Attribute",
31
+ stage = "self",
32
+ apply = function(
33
+ self: Types.SpecialKey,
34
+ scope: Types.Scope<unknown>,
35
+ value: unknown,
36
+ applyTo: Instance
37
+ )
38
+ if castToState(value) then
39
+ local value = value :: Types.StateObject<unknown>
40
+ checkLifetime.bOutlivesA(
41
+ scope, applyTo,
42
+ value.scope, value.oldestTask,
43
+ checkLifetime.formatters.boundAttribute, attributeName
44
+ )
45
+ Observer(scope, value :: any):onBind(function()
46
+ applyTo:SetAttribute(attributeName, peek(value))
47
+ end)
48
+ else
49
+ applyTo:SetAttribute(attributeName, value)
50
+ end
51
+ end
52
+ }
53
+ keyCache[attributeName] = key
54
+ end
55
+ return key
56
+ end
57
+
58
+ return Attribute
@@ -0,0 +1,47 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ --[[
7
+ A special key for property tables, which allows users to connect to
8
+ an attribute change on an instance.
9
+ ]]
10
+
11
+ local Package = script.Parent.Parent
12
+ local Types = require(Package.Types)
13
+ local External = require(Package.External)
14
+
15
+ local keyCache: {[string]: Types.SpecialKey} = {}
16
+
17
+ local function AttributeChange(
18
+ attributeName: string
19
+ ): Types.SpecialKey
20
+ local key = keyCache[attributeName]
21
+ if key == nil then
22
+ key = {
23
+ type = "SpecialKey",
24
+ kind = "AttributeChange",
25
+ stage = "observer",
26
+ apply = function(
27
+ self: Types.SpecialKey,
28
+ scope: Types.Scope<unknown>,
29
+ value: unknown,
30
+ applyTo: Instance
31
+ )
32
+ if typeof(value) ~= "function" then
33
+ External.logError("invalidAttributeChangeHandler", nil, attributeName)
34
+ end
35
+ local value = value :: (...unknown) -> (...unknown)
36
+ local event = applyTo:GetAttributeChangedSignal(attributeName)
37
+ table.insert(scope, event:Connect(function()
38
+ value((applyTo :: any):GetAttribute(attributeName))
39
+ end))
40
+ end
41
+ }
42
+ keyCache[attributeName] = key
43
+ end
44
+ return key
45
+ end
46
+
47
+ return AttributeChange
@@ -0,0 +1,63 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ --[[
7
+ A special key for property tables, which allows users to save instance attributes
8
+ into state objects
9
+ ]]
10
+
11
+ local Package = script.Parent.Parent
12
+ local Types = require(Package.Types)
13
+ local External = require(Package.External)
14
+ -- Memory
15
+ local checkLifetime = require(Package.Memory.checkLifetime)
16
+ -- State
17
+ local castToState = require(Package.State.castToState)
18
+
19
+ local keyCache: {[string]: Types.SpecialKey} = {}
20
+
21
+ local function AttributeOut(
22
+ attributeName: string
23
+ ): Types.SpecialKey
24
+ local key = keyCache[attributeName]
25
+ if key == nil then
26
+ key = {
27
+ type = "SpecialKey",
28
+ kind = "AttributeOut",
29
+ stage = "observer",
30
+ apply = function(
31
+ self: Types.SpecialKey,
32
+ scope: Types.Scope<unknown>,
33
+ value: unknown,
34
+ applyTo: Instance
35
+ )
36
+ local event = applyTo:GetAttributeChangedSignal(attributeName)
37
+
38
+ if not castToState(value) then
39
+ External.logError("invalidAttributeOutType")
40
+ end
41
+ local value = value :: Types.StateObject<unknown>
42
+ if value.kind ~= "Value" then
43
+ External.logError("invalidAttributeOutType")
44
+ end
45
+ local value = value :: Types.Value<unknown>
46
+ checkLifetime.bOutlivesA(
47
+ scope, applyTo,
48
+ value.scope, value.oldestTask,
49
+ checkLifetime.formatters.attributeOutputsTo, attributeName
50
+ )
51
+
52
+ value:set((applyTo :: any):GetAttribute(attributeName))
53
+ table.insert(scope, event:Connect(function()
54
+ value:set((applyTo :: any):GetAttribute(attributeName))
55
+ end))
56
+ end
57
+ }
58
+ keyCache[attributeName] = key
59
+ end
60
+ return key
61
+ end
62
+
63
+ return AttributeOut
@@ -0,0 +1,21 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ --[[
7
+ Helper function for type checking purposes. Casts the input to a `Child`
8
+ type, while constraining the input to be an array of `Child` - this prevents
9
+ Luau from erroneously inferring a different array type for the input.
10
+ ]]
11
+
12
+ local Package = script.Parent.Parent
13
+ local Types = require(Package.Types)
14
+
15
+ local function Child(
16
+ x: {Types.Child}
17
+ ): Types.Child
18
+ return x
19
+ end
20
+
21
+ return Child
@@ -0,0 +1,148 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ --[[
7
+ A special key for property tables, which parents any given descendants into
8
+ an instance.
9
+ ]]
10
+
11
+ local Package = script.Parent.Parent
12
+ local Types = require(Package.Types)
13
+ local External = require(Package.External)
14
+ local Observer = require(Package.Graph.Observer)
15
+ local peek = require(Package.State.peek)
16
+ local castToState = require(Package.State.castToState)
17
+ local doCleanup = require(Package.Memory.doCleanup)
18
+
19
+ type Set<T> = {[T]: unknown}
20
+
21
+ -- Experimental flag: name children based on the key used in the [Children] table
22
+ local EXPERIMENTAL_AUTO_NAMING = false
23
+
24
+ return {
25
+ type = "SpecialKey",
26
+ kind = "Children",
27
+ stage = "descendants",
28
+ apply = function(
29
+ self: Types.SpecialKey,
30
+ scope: Types.Scope<unknown>,
31
+ value: unknown,
32
+ applyTo: Instance
33
+ )
34
+ local newParented: Set<Instance> = {}
35
+ local oldParented: Set<Instance> = {}
36
+
37
+ -- save scopes for state object observers
38
+ local newScopes: {[Types.StateObject<unknown>]: Types.Scope<unknown>} = {}
39
+ local oldScopes: {[Types.StateObject<unknown>]: Types.Scope<unknown>} = {}
40
+
41
+ -- Rescans this key's value to find new instances to parent and state objects
42
+ -- to observe for changes; then unparents instances no longer found and
43
+ -- disconnects observers for state objects no longer present.
44
+ local function updateChildren()
45
+ oldParented, newParented = newParented, oldParented
46
+ oldScopes, newScopes = newScopes, oldScopes
47
+
48
+ local function processChild(
49
+ child: unknown,
50
+ autoName: string?
51
+ )
52
+ local childType = typeof(child)
53
+
54
+ if childType == "Instance" then
55
+ -- case 1; single instance
56
+ local child = child :: Instance
57
+
58
+ newParented[child] = true
59
+ if oldParented[child] == nil then
60
+ -- wasn't previously present
61
+
62
+ -- TODO: check for ancestry conflicts here
63
+ child.Parent = applyTo
64
+ else
65
+ -- previously here; we want to reuse, so remove from old
66
+ -- set so we don't encounter it during unparenting
67
+ oldParented[child] = nil
68
+ end
69
+
70
+ if EXPERIMENTAL_AUTO_NAMING and autoName ~= nil then
71
+ child.Name = autoName
72
+ end
73
+
74
+ elseif castToState(child) then
75
+ -- case 2; state object
76
+ local child = child :: Types.StateObject<unknown>
77
+
78
+ local value = peek(child)
79
+ -- allow nil to represent the absence of a child
80
+ if value ~= nil then
81
+ processChild(value, autoName)
82
+ end
83
+
84
+ local childScope = oldScopes[child]
85
+ if childScope == nil then
86
+ -- wasn't previously present
87
+ childScope = {}
88
+ Observer(childScope, child):onChange(updateChildren)
89
+ else
90
+ -- previously here; we want to reuse, so remove from old
91
+ -- set so we don't encounter it during unparenting
92
+ oldScopes[child] = nil
93
+ end
94
+
95
+ newScopes[child] = childScope
96
+
97
+ elseif childType == "table" then
98
+ -- case 3; table of objects
99
+ local child = child :: {[unknown]: unknown}
100
+
101
+ for key, subChild in pairs(child) do
102
+ local keyType = typeof(key)
103
+ local subAutoName: string? = nil
104
+
105
+ if keyType == "string" then
106
+ local key = key :: string
107
+ subAutoName = key
108
+ elseif keyType == "number" and autoName ~= nil then
109
+ local key = key :: number
110
+ subAutoName = autoName .. "_" .. key
111
+ end
112
+
113
+ processChild(subChild, subAutoName)
114
+ end
115
+
116
+ else
117
+ External.logWarn("unrecognisedChildType", childType)
118
+ end
119
+ end
120
+
121
+ if value ~= nil then
122
+ -- `propValue` is set to nil on cleanup, so we don't process children
123
+ -- in that case
124
+ processChild(value)
125
+ end
126
+
127
+ -- unparent any children that are no longer present
128
+ for oldInstance in pairs(oldParented) do
129
+ oldInstance.Parent = nil
130
+ end
131
+ table.clear(oldParented)
132
+
133
+ -- disconnect observers which weren't reused
134
+ for oldState, childScope in pairs(oldScopes) do
135
+ doCleanup(childScope)
136
+ end
137
+ table.clear(oldScopes)
138
+ end
139
+
140
+ table.insert(scope, function()
141
+ value = nil
142
+ updateChildren()
143
+ end)
144
+
145
+ -- perform initial child parenting
146
+ updateChildren()
147
+ end
148
+ } :: Types.SpecialKey
@@ -0,0 +1,33 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ --[[
7
+ Processes and returns an existing instance, with options for setting
8
+ properties, event handlers and other attributes on the instance.
9
+ ]]
10
+
11
+ local Package = script.Parent.Parent
12
+ local Types = require(Package.Types)
13
+ local External = require(Package.External)
14
+ local applyInstanceProps = require(Package.Instances.applyInstanceProps)
15
+
16
+ local function Hydrate(
17
+ scope: Types.Scope<unknown>,
18
+ target: Instance
19
+ )
20
+ if target :: any == nil then
21
+ External.logError("scopeMissing", nil, "instances using Hydrate", "myScope:Hydrate (instance) { ... }")
22
+ end
23
+ return function(
24
+ props: Types.PropertyTable
25
+ ): Instance
26
+
27
+ table.insert(scope, target)
28
+ applyInstanceProps(scope, props, target)
29
+ return target
30
+ end
31
+ end
32
+
33
+ return Hydrate
@@ -0,0 +1,53 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ --[[
7
+ Constructs and returns a new instance, with options for setting properties,
8
+ event handlers and other attributes on the instance right away.
9
+ ]]
10
+
11
+ local Package = script.Parent.Parent
12
+ local Types = require(Package.Types)
13
+ local External = require(Package.External)
14
+ local defaultProps = require(Package.Instances.defaultProps)
15
+ local applyInstanceProps = require(Package.Instances.applyInstanceProps)
16
+
17
+ type Component = (Types.PropertyTable) -> Instance
18
+
19
+ local function New(
20
+ scope: Types.Scope<unknown>,
21
+ className: string
22
+ )
23
+ if (className :: any) == nil then
24
+ local scope = (scope :: any) :: string
25
+ External.logError("scopeMissing", nil, "instances using New", "myScope:New \"" .. scope .. "\" { ... }")
26
+ end
27
+
28
+ -- This might look appealing to try and cache. But please don't. The scope
29
+ -- upvalue is shared between the two curried function calls, so this will
30
+ -- open incredible cross-codebase wormholes like you've never seen before.
31
+ return function(
32
+ props: Types.PropertyTable
33
+ ): Instance
34
+ local ok, instance = pcall(Instance.new, className)
35
+ if not ok then
36
+ External.logError("cannotCreateClass", nil, className)
37
+ end
38
+
39
+ local classDefaults = defaultProps[className]
40
+ if classDefaults ~= nil then
41
+ for defaultProp, defaultValue in pairs(classDefaults) do
42
+ (instance :: any)[defaultProp] = defaultValue
43
+ end
44
+ end
45
+
46
+ table.insert(scope, instance)
47
+ applyInstanceProps(scope, props, instance)
48
+
49
+ return instance
50
+ end
51
+ end
52
+
53
+ return New
@@ -0,0 +1,50 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ --[[
7
+ Constructs special keys for property tables which connect property change
8
+ listeners to an instance.
9
+ ]]
10
+
11
+ local Package = script.Parent.Parent
12
+ local Types = require(Package.Types)
13
+ local External = require(Package.External)
14
+
15
+ local keyCache: {[string]: Types.SpecialKey} = {}
16
+
17
+ local function OnChange(
18
+ propertyName: string
19
+ ): Types.SpecialKey
20
+ local key = keyCache[propertyName]
21
+ if key == nil then
22
+ key = {
23
+ type = "SpecialKey",
24
+ kind = "OnChange",
25
+ stage = "observer",
26
+ apply = function(
27
+ self: Types.SpecialKey,
28
+ scope: Types.Scope<unknown>,
29
+ callback: unknown,
30
+ applyTo: Instance
31
+ )
32
+ local ok, event = pcall(applyTo.GetPropertyChangedSignal, applyTo, propertyName)
33
+ if not ok then
34
+ External.logError("cannotConnectChange", nil, applyTo.ClassName, propertyName)
35
+ elseif typeof(callback) ~= "function" then
36
+ External.logError("invalidChangeHandler", nil, propertyName)
37
+ else
38
+ local callback = callback :: (...unknown) -> (...unknown)
39
+ table.insert(scope, event:Connect(function()
40
+ callback((applyTo :: any)[propertyName])
41
+ end))
42
+ end
43
+ end
44
+ }
45
+ keyCache[propertyName] = key
46
+ end
47
+ return key
48
+ end
49
+
50
+ return OnChange
@@ -0,0 +1,54 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ --[[
7
+ Constructs special keys for property tables which connect event listeners to
8
+ an instance.
9
+ ]]
10
+
11
+ local Package = script.Parent.Parent
12
+ local Types = require(Package.Types)
13
+ local External = require(Package.External)
14
+
15
+ local keyCache: {[string]: Types.SpecialKey} = {}
16
+
17
+ local function getProperty_unsafe(
18
+ instance: Instance,
19
+ property: string
20
+ )
21
+ return (instance :: any)[property]
22
+ end
23
+
24
+ local function OnEvent(
25
+ eventName: string
26
+ ): Types.SpecialKey
27
+ local key = keyCache[eventName]
28
+ if key == nil then
29
+ key = {
30
+ type = "SpecialKey",
31
+ kind = "OnEvent",
32
+ stage = "observer",
33
+ apply = function(
34
+ self: Types.SpecialKey,
35
+ scope: Types.Scope<unknown>,
36
+ callback: unknown,
37
+ applyTo: Instance
38
+ )
39
+ local ok, event = pcall(getProperty_unsafe, applyTo, eventName)
40
+ if not ok or typeof(event) ~= "RBXScriptSignal" then
41
+ External.logError("cannotConnectEvent", nil, applyTo.ClassName, eventName)
42
+ elseif typeof(callback) ~= "function" then
43
+ External.logError("invalidEventHandler", nil, eventName)
44
+ else
45
+ table.insert(scope, event:Connect(callback :: any))
46
+ end
47
+ end
48
+ }
49
+ keyCache[eventName] = key
50
+ end
51
+ return key
52
+ end
53
+
54
+ return OnEvent
@@ -0,0 +1,69 @@
1
+ --!strict
2
+ --!nolint LocalUnused
3
+ --!nolint LocalShadow
4
+ local task = nil -- Disable usage of Roblox's task scheduler
5
+
6
+ --[[
7
+ A special key for property tables, which allows users to extract values from
8
+ an instance into an automatically-updated Value object.
9
+ ]]
10
+
11
+ local Package = script.Parent.Parent
12
+ local Types = require(Package.Types)
13
+ local External = require(Package.External)
14
+ -- Memory
15
+ local checkLifetime = require(Package.Memory.checkLifetime)
16
+ -- State
17
+ local castToState = require(Package.State.castToState)
18
+
19
+ local keyCache: {[string]: Types.SpecialKey} = {}
20
+
21
+ local function Out(
22
+ propertyName: string
23
+ ): Types.SpecialKey
24
+ local key = keyCache[propertyName]
25
+ if key == nil then
26
+ key = {
27
+ type = "SpecialKey",
28
+ kind = "Out",
29
+ stage = "observer",
30
+ apply = function(
31
+ self: Types.SpecialKey,
32
+ scope: Types.Scope<unknown>,
33
+ value: unknown,
34
+ applyTo: Instance
35
+ )
36
+ local ok, event = pcall(applyTo.GetPropertyChangedSignal, applyTo, propertyName)
37
+ if not ok then
38
+ External.logError("invalidOutProperty", nil, applyTo.ClassName, propertyName)
39
+ end
40
+
41
+ if not castToState(value) then
42
+ External.logError("invalidOutType")
43
+ end
44
+ local value = value :: Types.StateObject<unknown>
45
+ if value.kind ~= "Value" then
46
+ External.logError("invalidOutType")
47
+ end
48
+ local value = value :: Types.Value<unknown>
49
+ checkLifetime.bOutlivesA(
50
+ scope, applyTo,
51
+ value.scope, value.oldestTask,
52
+ checkLifetime.formatters.propertyOutputsTo, propertyName
53
+ )
54
+
55
+ value:set((applyTo :: any)[propertyName])
56
+ table.insert(
57
+ scope,
58
+ event:Connect(function()
59
+ value:set((applyTo :: any)[propertyName])
60
+ end)
61
+ )
62
+ end
63
+ }
64
+ keyCache[propertyName] = key
65
+ end
66
+ return key
67
+ end
68
+
69
+ return Out