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,787 @@
1
+ --!strict
2
+
3
+ local RunService = game:GetService("RunService")
4
+
5
+ export type Trove = {
6
+ Extend: (self: Trove) -> Trove,
7
+ Clone: <T>(self: Trove, instance: T & Instance) -> T,
8
+ Construct: <T, A...>(self: Trove, class: Constructable<T, A...>, A...) -> T,
9
+ Connect: (
10
+ self: Trove,
11
+ signal: SignalLike | SignalLikeMetatable | RBXScriptSignal,
12
+ fn: (...any) -> ...any
13
+ ) -> ConnectionLike | ConnectionLikeMetatable,
14
+ BindToRenderStep: (self: Trove, name: string, priority: number, fn: (dt: number) -> ()) -> (),
15
+ AddPromise: <T>(self: Trove, promise: (T & PromiseLike) | (T & PromiseLikeMetatable)) -> T,
16
+ Add: <T>(self: Trove, object: T & Trackable, cleanupMethod: string?) -> T,
17
+ Remove: <T>(self: Trove, object: T & Trackable) -> boolean,
18
+ Pop: <T>(self: Trove, object: T & Trackable) -> boolean,
19
+ Clean: (self: Trove) -> (),
20
+ WrapClean: (self: Trove) -> () -> (),
21
+ AttachToInstance: (self: Trove, instance: Instance) -> RBXScriptConnection,
22
+ Destroy: (self: Trove) -> (),
23
+ }
24
+
25
+ type TroveInternal = Trove & {
26
+ _objects: { any },
27
+ _cleaning: boolean,
28
+ _findAndRemoveFromObjects: (self: TroveInternal, object: any, cleanup: boolean) -> boolean,
29
+ _cleanupObject: (self: TroveInternal, object: any, cleanupMethod: string?) -> (),
30
+ }
31
+
32
+ --[=[
33
+ @within Trove
34
+ @type Trackable Instance | RBXScriptConnection | ConnectionLike | ConnectLikeMetatable | PromiseLike | PromiseLikeMetatable | thread | ((...any) -> ...any) | Destroyable | DestroyableMetatable | DestroyableLowercase | DestroyableLowercaseMetatable | Disconnectable | DisconnectableMetatable | DisconnectableLowercase | DisconnectableLowercaseMetatable | SignalLike | SignalLikeMetatable
35
+ Represents all trackable objects by Trove.
36
+ ]=]
37
+ export type Trackable =
38
+ | Instance
39
+ | RBXScriptConnection
40
+ | ConnectionLike
41
+ | ConnectionLikeMetatable
42
+ | PromiseLike
43
+ | PromiseLikeMetatable
44
+ | thread
45
+ | ((...any) -> ...any)
46
+ | Destroyable
47
+ | DestroyableMetatable
48
+ | DestroyableLowercase
49
+ | DestroyableLowercaseMetatable
50
+ | Disconnectable
51
+ | DisconectableMetatable
52
+ | DisconnectableLowercase
53
+ | DisconnectableLowercaseMetatable
54
+ | SignalLike
55
+ | SignalLikeMetatable
56
+
57
+ --[=[
58
+ @within Trove
59
+ @interface ConnectionLike
60
+ .Connected boolean
61
+ .Disconnect (self) -> ()
62
+ ]=]
63
+ type ConnectionLike = {
64
+ Connected: boolean,
65
+ Disconnect: (self: ConnectionLike) -> (),
66
+ }
67
+
68
+ --[=[
69
+ @within Trove
70
+ @interface ConnectionLikeMetatable
71
+ .Connected boolean
72
+ .Disconnect (self) -> ()
73
+ @tag Metatable
74
+ ]=]
75
+ type ConnectionLikeMetatable = typeof(setmetatable(
76
+ {},
77
+ {} :: { Connected: boolean, Disconnect: (self: ConnectionLikeMetatable) -> () }
78
+ ))
79
+
80
+ --[=[
81
+ @within Trove
82
+ @interface SignalLike
83
+ .Connect (self, callback: (...any) -> ...any) -> ConnectionLike
84
+ .Once (self, callback: (...any) -> ...any) -> ConnectionLike
85
+ ]=]
86
+ type SignalLike = {
87
+ Connect: (self: SignalLike, callback: (...any) -> ...any) -> ConnectionLike | ConnectionLikeMetatable,
88
+ Once: (self: SignalLike, callback: (...any) -> ...any) -> ConnectionLike | ConnectionLikeMetatable,
89
+ }
90
+
91
+ --[=[
92
+ @within Trove
93
+ @interface SignalLikeMetatable
94
+ .Connect (self, callback: (...any) -> ...any) -> ConnectionLike
95
+ .Once (self, callback: (...any) -> ...any) -> ConnectionLike
96
+ @tag Metatable
97
+ ]=]
98
+ type SignalLikeMetatable = typeof(setmetatable(
99
+ {},
100
+ {} :: {
101
+ Connect: (self: SignalLikeMetatable, callback: (...any) -> ...any) -> ConnectionLike | ConnectionLikeMetatable,
102
+ Once: (self: SignalLikeMetatable, callback: (...any) -> ...any) -> ConnectionLike | ConnectionLikeMetatable,
103
+ }
104
+ ))
105
+
106
+ --[=[
107
+ @within Trove
108
+ @interface PromiseLike
109
+ .getStatus (self) -> string
110
+ .finally (self, callback: (...any) -> ...any) -> PromiseLike
111
+ .cancel (self) -> ()
112
+ ]=]
113
+ type PromiseLike = {
114
+ getStatus: (self: PromiseLike) -> string,
115
+ finally: (self: PromiseLike, callback: (...any) -> ...any) -> PromiseLike | PromiseLikeMetatable,
116
+ cancel: (self: PromiseLike) -> (),
117
+ }
118
+
119
+ --[=[
120
+ @within Trove
121
+ @interface PromiseLikeMetatable
122
+ .getStatus (self) -> string
123
+ .finally (self, callback: (...any) -> ...any) -> PromiseLike
124
+ .cancel (self) -> ()
125
+ @tag Metatable
126
+ ]=]
127
+ type PromiseLikeMetatable = typeof(setmetatable(
128
+ {},
129
+ {} :: {
130
+ getStatus: (self: any) -> string,
131
+ finally: (self: PromiseLikeMetatable, callback: (...any) -> ...any) -> PromiseLike | PromiseLikeMetatable,
132
+ cancel: (self: PromiseLikeMetatable) -> (),
133
+ }
134
+ ))
135
+
136
+ --[=[
137
+ @within Trove
138
+ @type Constructable { new: (A...) -> T } | (A...) -> T
139
+ ]=]
140
+ type Constructable<T, A...> = { new: (A...) -> T } | (A...) -> T
141
+
142
+ --[=[
143
+ @within Trove
144
+ @interface Destroyable
145
+ .Destroy (self) -> ()
146
+ ]=]
147
+ type Destroyable = {
148
+ Destroy: (self: Destroyable) -> (),
149
+ }
150
+
151
+ --[=[
152
+ @within Trove
153
+ @interface DestroyableMetatable
154
+ .Destroy (self) -> ()
155
+ @tag Metatable
156
+ ]=]
157
+ type DestroyableMetatable = typeof(setmetatable({}, {} :: { Destroy: (self: DestroyableMetatable) -> () }))
158
+
159
+ --[=[
160
+ @within Trove
161
+ @interface DestroyableLowercase
162
+ .destroy (self) -> ()
163
+ ]=]
164
+ type DestroyableLowercase = {
165
+ destroy: (self: DestroyableLowercase) -> (),
166
+ }
167
+
168
+ --[=[
169
+ @within Trove
170
+ @interface DestroyableLowercaseMetatable
171
+ .destroy (self) -> ()
172
+ @tag Metatable
173
+ ]=]
174
+ type DestroyableLowercaseMetatable = typeof(setmetatable(
175
+ {},
176
+ {} :: { destroy: (self: DestroyableLowercaseMetatable) -> () }
177
+ ))
178
+
179
+ --[=[
180
+ @within Trove
181
+ @interface Disconnectable
182
+ .Disconnect (self) -> ()
183
+ ]=]
184
+ type Disconnectable = {
185
+ Disconnect: (self: Disconnectable) -> (),
186
+ }
187
+
188
+ --[=[
189
+ @within Trove
190
+ @interface DisconectableMetatable
191
+ .Disconnect (self) -> ()
192
+ @tag Metatable
193
+ ]=]
194
+ type DisconectableMetatable = typeof(setmetatable({}, {} :: { Disconnect: (self: DisconectableMetatable) -> () }))
195
+
196
+ --[=[
197
+ @within Trove
198
+ @interface DisconnectableLowercase
199
+ .disconnect (self) -> ()
200
+ ]=]
201
+ type DisconnectableLowercase = {
202
+ disconnect: (self: DisconnectableLowercase) -> (),
203
+ }
204
+
205
+ --[=[
206
+ @within Trove
207
+ @interface DisconnectableLowercaseMetatable
208
+ .disconnect (self) -> ()
209
+ @tag Metatable
210
+ ]=]
211
+ type DisconnectableLowercaseMetatable = typeof(setmetatable(
212
+ {},
213
+ {} :: { disconnect: (self: DisconnectableLowercaseMetatable) -> () }
214
+ ))
215
+
216
+ local FN_MARKER = newproxy()
217
+ local THREAD_MARKER = newproxy()
218
+ local GENERIC_OBJECT_CLEANUP_METHODS = table.freeze({ "Destroy", "Disconnect", "destroy", "disconnect" })
219
+
220
+ local function getObjectCleanupFunction(object: any, cleanupMethod: string?)
221
+ local t = typeof(object)
222
+
223
+ if t == "function" then
224
+ return FN_MARKER
225
+ elseif t == "thread" then
226
+ return THREAD_MARKER
227
+ end
228
+
229
+ if cleanupMethod then
230
+ return cleanupMethod
231
+ end
232
+
233
+ if t == "Instance" then
234
+ return "Destroy"
235
+ elseif t == "RBXScriptConnection" then
236
+ return "Disconnect"
237
+ elseif t == "table" then
238
+ for _, genericCleanupMethod in GENERIC_OBJECT_CLEANUP_METHODS do
239
+ if typeof(object[genericCleanupMethod]) == "function" then
240
+ return genericCleanupMethod
241
+ end
242
+ end
243
+ end
244
+
245
+ error(`failed to get cleanup function for object {t}: {object}`, 3)
246
+ end
247
+
248
+ local function assertPromiseLike(object: any)
249
+ if
250
+ typeof(object) ~= "table"
251
+ or typeof(object.getStatus) ~= "function"
252
+ or typeof(object.finally) ~= "function"
253
+ or typeof(object.cancel) ~= "function"
254
+ then
255
+ error("did not receive a promise as an argument", 3)
256
+ end
257
+ end
258
+
259
+ local function assertSignalLike(object: any)
260
+ if
261
+ typeof(object) ~= "RBXScriptSignal"
262
+ and (typeof(object) ~= "table" or typeof(object.Connect) ~= "function" or typeof(object.Once) ~= "function")
263
+ then
264
+ error("did not receive a signal as an argument", 3)
265
+ end
266
+ end
267
+
268
+ --[=[
269
+ @class Trove
270
+ A Trove is helpful for tracking any sort of object during
271
+ runtime that needs to get cleaned up at some point.
272
+ ]=]
273
+ local Trove = {}
274
+ Trove.__index = Trove
275
+
276
+ --[=[
277
+ @return Trove
278
+ Constructs a Trove object.
279
+
280
+ ```lua
281
+ local trove = Trove.new()
282
+ ```
283
+ ]=]
284
+ function Trove.new(): Trove
285
+ local self = setmetatable({}, Trove)
286
+
287
+ self._objects = {}
288
+ self._cleaning = false
289
+
290
+ return (self :: any) :: Trove
291
+ end
292
+
293
+ --[=[
294
+ @method Add
295
+ @within Trove
296
+ @param object any -- Object to track
297
+ @param cleanupMethod string? -- Optional cleanup name override
298
+ @return object: any
299
+ Adds an object to the trove. Once the trove is cleaned or
300
+ destroyed, the object will also be cleaned up.
301
+
302
+ The following types are accepted (e.g. `typeof(object)`):
303
+
304
+ | Type | Cleanup |
305
+ | ---- | ------- |
306
+ | `Instance` | `object:Destroy()` |
307
+ | `RBXScriptConnection` | `object:Disconnect()` |
308
+ | `function` | `object()` |
309
+ | `thread` | `task.cancel(object)` |
310
+ | `table` | `object:Destroy()` _or_ `object:Disconnect()` _or_ `object:destroy()` _or_ `object:disconnect()` |
311
+ | `table` with `cleanupMethod` | `object:<cleanupMethod>()` |
312
+
313
+ Returns the object added.
314
+
315
+ ```lua
316
+ -- Add a part to the trove, then destroy the trove,
317
+ -- which will also destroy the part:
318
+ local part = Instance.new("Part")
319
+ trove:Add(part)
320
+ trove:Destroy()
321
+
322
+ -- Add a function to the trove:
323
+ trove:Add(function()
324
+ print("Cleanup!")
325
+ end)
326
+ trove:Destroy()
327
+
328
+ -- Standard cleanup from table:
329
+ local tbl = {}
330
+ function tbl:Destroy()
331
+ print("Cleanup")
332
+ end
333
+ trove:Add(tbl)
334
+
335
+ -- Custom cleanup from table:
336
+ local tbl = {}
337
+ function tbl:DoSomething()
338
+ print("Do something on cleanup")
339
+ end
340
+ trove:Add(tbl, "DoSomething")
341
+ ```
342
+ ]=]
343
+ function Trove.Add(self: TroveInternal, object: Trackable, cleanupMethod: string?): any
344
+ if self._cleaning then
345
+ error("cannot call trove:Add() while cleaning", 2)
346
+ end
347
+
348
+ local cleanup = getObjectCleanupFunction(object, cleanupMethod)
349
+ table.insert(self._objects, { object, cleanup })
350
+
351
+ return object
352
+ end
353
+
354
+ --[=[
355
+ @method Clone
356
+ @within Trove
357
+ @return Instance
358
+ Clones the given instance and adds it to the trove. Shorthand for
359
+ `trove:Add(instance:Clone())`.
360
+
361
+ ```lua
362
+ local clonedPart = trove:Clone(somePart)
363
+ ```
364
+ ]=]
365
+ function Trove.Clone(self: TroveInternal, instance: Instance): Instance
366
+ if self._cleaning then
367
+ error("cannot call trove:Clone() while cleaning", 2)
368
+ end
369
+
370
+ return self:Add(instance:Clone())
371
+ end
372
+
373
+ --[=[
374
+ @method Construct
375
+ @within Trove
376
+ @param class { new(Args...) -> T } | (Args...) -> T
377
+ @param ... Args...
378
+ @return T
379
+ Constructs a new object from either the
380
+ table or function given.
381
+
382
+ If a table is given, the table's `new`
383
+ function will be called with the given
384
+ arguments.
385
+
386
+ If a function is given, the function will
387
+ be called with the given arguments.
388
+
389
+ The result from either of the two options
390
+ will be added to the trove.
391
+
392
+ This is shorthand for `trove:Add(SomeClass.new(...))`
393
+ and `trove:Add(SomeFunction(...))`.
394
+
395
+ ```lua
396
+ local Signal = require(somewhere.Signal)
397
+
398
+ -- All of these are identical:
399
+ local s = trove:Construct(Signal)
400
+ local s = trove:Construct(Signal.new)
401
+ local s = trove:Construct(function() return Signal.new() end)
402
+ local s = trove:Add(Signal.new())
403
+
404
+ -- Even Roblox instances can be created:
405
+ local part = trove:Construct(Instance, "Part")
406
+ ```
407
+ ]=]
408
+ function Trove.Construct<T, A...>(self: TroveInternal, class: Constructable<T, A...>, ...: A...)
409
+ if self._cleaning then
410
+ error("Cannot call trove:Construct() while cleaning", 2)
411
+ end
412
+
413
+ local object = nil
414
+ local t = type(class)
415
+ if t == "table" then
416
+ object = (class :: any).new(...)
417
+ elseif t == "function" then
418
+ object = (class :: any)(...)
419
+ end
420
+
421
+ return self:Add(object)
422
+ end
423
+
424
+ --[=[
425
+ @method Connect
426
+ @within Trove
427
+ @param signal RBXScriptSignal
428
+ @param fn (...: any) -> ()
429
+ @return RBXScriptConnection
430
+ Connects the function to the signal, adds the connection
431
+ to the trove, and then returns the connection.
432
+
433
+ This is shorthand for `trove:Add(signal:Connect(fn))`.
434
+
435
+ ```lua
436
+ trove:Connect(workspace.ChildAdded, function(instance)
437
+ print(instance.Name .. " added to workspace")
438
+ end)
439
+ ```
440
+ ]=]
441
+ function Trove.Connect(
442
+ self: TroveInternal,
443
+ signal: SignalLike | SignalLikeMetatable | RBXScriptSignal,
444
+ fn: (...any) -> ...any
445
+ )
446
+ if self._cleaning then
447
+ error("Cannot call trove:Connect() while cleaning", 2)
448
+ end
449
+ assertSignalLike(signal)
450
+
451
+ local confirmedSignal = signal :: SignalLike
452
+
453
+ return self:Add(confirmedSignal:Connect(fn))
454
+ end
455
+
456
+ --[=[
457
+ @method Once
458
+ @within Trove
459
+ @param signal RBXScriptSignal
460
+ @param fn (...: any) -> ()
461
+ @return RBXScriptConnection
462
+ Connects the function to the signal using the `Once`
463
+ method, adds the connection to the trove, and then
464
+ returns the connection.
465
+
466
+ This is shorthand for `trove:Add(signal:Once(fn))`.
467
+
468
+ ```lua
469
+ trove:Connect(workspace.ChildAdded, function(instance)
470
+ print(instance.Name .. " added to workspace")
471
+ end)
472
+ ```
473
+ ]=]
474
+ function Trove.Once(
475
+ self: TroveInternal,
476
+ signal: SignalLike | SignalLikeMetatable | RBXScriptSignal,
477
+ fn: (...any) -> ...any
478
+ )
479
+ if self._cleaning then
480
+ error("Cannot call trove:Connect() while cleaning", 2)
481
+ end
482
+ assertSignalLike(signal)
483
+
484
+ local confirmedSignal = signal :: SignalLike
485
+
486
+ local conn
487
+ conn = confirmedSignal:Once(function(...)
488
+ fn(...)
489
+ self:Pop(conn)
490
+ end)
491
+
492
+ return self:Add(conn)
493
+ end
494
+
495
+ --[=[
496
+ @method BindToRenderStep
497
+ @within Trove
498
+ @param name string
499
+ @param priority number
500
+ @param fn (dt: number) -> ()
501
+ Calls `RunService:BindToRenderStep` and registers a function in the
502
+ trove that will call `RunService:UnbindFromRenderStep` on cleanup.
503
+
504
+ ```lua
505
+ trove:BindToRenderStep("Test", Enum.RenderPriority.Last.Value, function(dt)
506
+ -- Do something
507
+ end)
508
+ ```
509
+ ]=]
510
+ function Trove.BindToRenderStep(self: TroveInternal, name: string, priority: number, fn: (dt: number) -> ())
511
+ if self._cleaning then
512
+ error("cannot call trove:BindToRenderStep() while cleaning", 2)
513
+ end
514
+
515
+ RunService:BindToRenderStep(name, priority, fn)
516
+
517
+ self:Add(function()
518
+ RunService:UnbindFromRenderStep(name)
519
+ end)
520
+ end
521
+
522
+ --[=[
523
+ @method AddPromise
524
+ @within Trove
525
+ @param promise Promise
526
+ @return Promise
527
+ Gives the promise to the trove, which will cancel the promise if the trove is cleaned up or if the promise
528
+ is removed. The exact promise is returned, thus allowing chaining.
529
+
530
+ ```lua
531
+ trove:AddPromise(doSomethingThatReturnsAPromise())
532
+ :andThen(function()
533
+ print("Done")
534
+ end)
535
+ -- Will cancel the above promise (assuming it didn't resolve immediately)
536
+ trove:Clean()
537
+
538
+ local p = trove:AddPromise(doSomethingThatReturnsAPromise())
539
+ -- Will also cancel the promise
540
+ trove:Remove(p)
541
+ ```
542
+
543
+ :::caution Promise v4 Only
544
+ This is only compatible with the [roblox-lua-promise](https://eryn.io/roblox-lua-promise/) library, version 4.
545
+ :::
546
+ ]=]
547
+ function Trove.AddPromise(self: TroveInternal, promise: PromiseLike | PromiseLikeMetatable)
548
+ if self._cleaning then
549
+ error("cannot call trove:AddPromise() while cleaning", 2)
550
+ end
551
+ assertPromiseLike(promise)
552
+ local confirmedPromise = promise :: PromiseLike
553
+
554
+ if confirmedPromise:getStatus() == "Started" then
555
+ confirmedPromise:finally(function()
556
+ if self._cleaning then
557
+ return
558
+ end
559
+ self:_findAndRemoveFromObjects(confirmedPromise, false)
560
+ end)
561
+
562
+ self:Add(confirmedPromise, "cancel")
563
+ end
564
+
565
+ return confirmedPromise
566
+ end
567
+
568
+ --[=[
569
+ @method Remove
570
+ @within Trove
571
+ @param object any
572
+ Removes the object from the Trove and cleans it up. To remove without
573
+ cleaning, use the `Pop` method instead.
574
+
575
+ ```lua
576
+ local part = Instance.new("Part")
577
+ trove:Add(part)
578
+ trove:Remove(part) -- Part is destroyed
579
+ ```
580
+ ]=]
581
+ function Trove.Remove(self: TroveInternal, object: Trackable): boolean
582
+ if self._cleaning then
583
+ error("cannot call trove:Remove() while cleaning", 2)
584
+ end
585
+
586
+ return self:_findAndRemoveFromObjects(object, true)
587
+ end
588
+
589
+ --[=[
590
+ @method Pop
591
+ @within Trove
592
+ @param object any
593
+ Removes the object from the Trove, but does _not_ clean it up. To
594
+ clean up the object on removal, use the `Remove` method instead.
595
+
596
+ ```lua
597
+ local part = Instance.new("Part")
598
+ trove:Add(part)
599
+ trove:Pop(part)
600
+ trove:Clean() -- Part is NOT destroyed
601
+ ```
602
+ ]=]
603
+ function Trove.Pop(self: TroveInternal, object: Trackable): boolean
604
+ if self._cleaning then
605
+ error("cannot call trove:Pop() while cleaning", 2)
606
+ end
607
+
608
+ return self:_findAndRemoveFromObjects(object, false)
609
+ end
610
+
611
+ --[=[
612
+ @method Extend
613
+ @within Trove
614
+ @return Trove
615
+ Creates and adds another trove to itself. This is just shorthand
616
+ for `trove:Construct(Trove)`. This is useful for contexts where
617
+ the trove object is present, but the class itself isn't.
618
+
619
+ :::note
620
+ This does _not_ clone the trove. In other words, the objects in the
621
+ trove are not given to the new constructed trove. This is simply to
622
+ construct a new Trove and add it as an object to track.
623
+ :::
624
+
625
+ ```lua
626
+ local trove = Trove.new()
627
+ local subTrove = trove:Extend()
628
+
629
+ trove:Clean() -- Cleans up the subTrove too
630
+ ```
631
+ ]=]
632
+ function Trove.Extend(self: TroveInternal)
633
+ if self._cleaning then
634
+ error("cannot call trove:Extend() while cleaning", 2)
635
+ end
636
+
637
+ return self:Construct(Trove)
638
+ end
639
+
640
+ --[=[
641
+ @method Clean
642
+ @within Trove
643
+ Cleans up all objects in the trove. This is
644
+ similar to calling `Remove` on each object
645
+ within the trove. The ordering of the objects
646
+ removed is _not_ guaranteed.
647
+
648
+ ```lua
649
+ trove:Clean()
650
+ ```
651
+ ]=]
652
+ function Trove.Clean(self: TroveInternal)
653
+ if self._cleaning then
654
+ return
655
+ end
656
+
657
+ self._cleaning = true
658
+
659
+ for _, obj in self._objects do
660
+ self:_cleanupObject(obj[1], obj[2])
661
+ end
662
+
663
+ table.clear(self._objects)
664
+ self._cleaning = false
665
+ end
666
+
667
+ --[=[
668
+ @method WrapClean
669
+ @within Trove
670
+ Returns a function that wraps the trove's `Clean()`
671
+ method. Calling the returned function will clean up
672
+ the trove.
673
+
674
+ This is often useful in contexts where functions
675
+ are the primary mode for cleaning up an environment,
676
+ such as in many "observer" patterns.
677
+
678
+ ```lua
679
+ local cleanup = trove:WrapClean()
680
+
681
+ -- Sometime later...
682
+ cleanup()
683
+ ```
684
+
685
+ ```lua
686
+ -- Common observer pattern example:
687
+ someObserver(function()
688
+ local trove = Trove.new()
689
+ -- Foo
690
+ return trove:WrapClean()
691
+ end)
692
+ ```
693
+ ]=]
694
+ function Trove.WrapClean(self: TroveInternal)
695
+ return function()
696
+ self:Clean()
697
+ end
698
+ end
699
+
700
+ function Trove._findAndRemoveFromObjects(self: TroveInternal, object: any, cleanup: boolean): boolean
701
+ local objects = self._objects
702
+
703
+ for i, obj in objects do
704
+ if obj[1] == object then
705
+ local n = #objects
706
+ objects[i] = objects[n]
707
+ objects[n] = nil
708
+
709
+ if cleanup then
710
+ self:_cleanupObject(obj[1], obj[2])
711
+ end
712
+
713
+ return true
714
+ end
715
+ end
716
+
717
+ return false
718
+ end
719
+
720
+ function Trove._cleanupObject(_self: TroveInternal, object: any, cleanupMethod: string?)
721
+ if cleanupMethod == FN_MARKER then
722
+ task.spawn(object)
723
+ elseif cleanupMethod == THREAD_MARKER then
724
+ pcall(task.cancel, object)
725
+ else
726
+ object[cleanupMethod](object)
727
+ end
728
+ end
729
+
730
+ --[=[
731
+ @method AttachToInstance
732
+ @within Trove
733
+ @param instance Instance
734
+ @return RBXScriptConnection
735
+ Attaches the trove to a Roblox instance. Once this
736
+ instance is removed from the game (parent or ancestor's
737
+ parent set to `nil`), the trove will automatically
738
+ clean up.
739
+
740
+ This inverses the ownership of the Trove object, and should
741
+ only be used when necessary. In other words, the attached
742
+ instance dictates when the trove is cleaned up, rather than
743
+ the trove dictating the cleanup of the instance.
744
+
745
+ :::caution
746
+ Will throw an error if `instance` is not a descendant
747
+ of the game hierarchy.
748
+ :::
749
+
750
+ ```lua
751
+ trove:AttachToInstance(somePart)
752
+ trove:Add(function()
753
+ print("Cleaned")
754
+ end)
755
+
756
+ -- Destroying the part will cause the trove to clean up, thus "Cleaned" printed:
757
+ somePart:Destroy()
758
+ ```
759
+ ]=]
760
+ function Trove.AttachToInstance(self: TroveInternal, instance: Instance)
761
+ if self._cleaning then
762
+ error("cannot call trove:AttachToInstance() while cleaning", 2)
763
+ elseif not instance:IsDescendantOf(game) then
764
+ error("instance is not a descendant of the game hierarchy", 2)
765
+ end
766
+
767
+ return self:Connect(instance.Destroying, function()
768
+ self:Destroy()
769
+ end)
770
+ end
771
+
772
+ --[=[
773
+ @method Destroy
774
+ @within Trove
775
+ Alias for `trove:Clean()`.
776
+
777
+ ```lua
778
+ trove:Destroy()
779
+ ```
780
+ ]=]
781
+ function Trove.Destroy(self: TroveInternal)
782
+ self:Clean()
783
+ end
784
+
785
+ return {
786
+ new = Trove.new,
787
+ }