roblox-opencode 1.0.0 → 1.0.2

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 (246) hide show
  1. package/README.md +112 -122
  2. package/commands/setup-game.md +108 -108
  3. package/commands/sync-check.md +53 -53
  4. package/core/roblox-core.md +93 -93
  5. package/dist/server.js +189 -167
  6. package/package.json +35 -35
  7. package/skills/roblox-analytics/SKILL.md +277 -277
  8. package/skills/roblox-analytics/references/event-batcher.luau +75 -75
  9. package/skills/roblox-animation-vfx/SKILL.md +1325 -1325
  10. package/skills/roblox-architecture/SKILL.md +877 -863
  11. package/skills/roblox-architecture/references/combat-systems.md +1381 -1381
  12. package/skills/roblox-code-review/SKILL.md +686 -686
  13. package/skills/roblox-data/SKILL.md +889 -889
  14. package/skills/roblox-data/references/inventory-systems.md +1729 -1729
  15. package/skills/roblox-debug/SKILL.md +98 -98
  16. package/skills/roblox-gui/SKILL.md +1103 -1103
  17. package/skills/roblox-gui-fusion/SKILL.md +150 -150
  18. package/skills/roblox-gui-fusion/references/inventory.luau +427 -427
  19. package/skills/roblox-gui-fusion/references/settings-menu.luau +579 -579
  20. package/skills/roblox-gui-fusion/references/shop.luau +411 -411
  21. package/skills/roblox-luau-mastery/SKILL.md +1618 -1519
  22. package/skills/roblox-monetization/SKILL.md +1084 -1084
  23. package/skills/roblox-monetization/references/process-receipt.luau +131 -131
  24. package/skills/roblox-networking/SKILL.md +669 -669
  25. package/skills/roblox-networking/references/remote-validator.luau +193 -193
  26. package/skills/roblox-publish-checklist/SKILL.md +127 -127
  27. package/skills/roblox-runtime/SKILL.md +753 -753
  28. package/skills/roblox-sharp-edges/SKILL.md +294 -294
  29. package/skills/roblox-sync/SKILL.md +126 -126
  30. package/skills/roblox-testing/SKILL.md +943 -943
  31. package/skills/roblox-tooling/SKILL.md +149 -149
  32. package/vendor/LICENSES/ProfileStore-LICENSE +201 -201
  33. package/vendor/LICENSES/RbxUtil-LICENSE +7 -7
  34. package/vendor/LICENSES/promise-LICENSE +20 -20
  35. package/vendor/LICENSES/t-LICENSE +21 -21
  36. package/vendor/LICENSES/testez-LICENSE +200 -200
  37. package/vendor/README.md +83 -83
  38. package/vendor/fusion/Animation/ExternalTime.luau +83 -83
  39. package/vendor/fusion/Animation/Spring.luau +321 -321
  40. package/vendor/fusion/Animation/Stopwatch.luau +127 -127
  41. package/vendor/fusion/Animation/Tween.luau +187 -187
  42. package/vendor/fusion/Animation/getTweenDuration.luau +27 -27
  43. package/vendor/fusion/Animation/getTweenRatio.luau +47 -47
  44. package/vendor/fusion/Animation/lerpType.luau +163 -163
  45. package/vendor/fusion/Animation/packType.luau +99 -99
  46. package/vendor/fusion/Animation/springCoefficients.luau +80 -80
  47. package/vendor/fusion/Animation/unpackType.luau +102 -102
  48. package/vendor/fusion/Colour/Oklab.luau +70 -70
  49. package/vendor/fusion/Colour/sRGB.luau +54 -54
  50. package/vendor/fusion/External.luau +167 -167
  51. package/vendor/fusion/ExternalDebug.luau +69 -69
  52. package/vendor/fusion/Graph/Observer.luau +113 -113
  53. package/vendor/fusion/Graph/castToGraph.luau +28 -28
  54. package/vendor/fusion/Graph/change.luau +80 -80
  55. package/vendor/fusion/Graph/depend.luau +32 -32
  56. package/vendor/fusion/Graph/evaluate.luau +55 -55
  57. package/vendor/fusion/Instances/Attribute.luau +57 -57
  58. package/vendor/fusion/Instances/AttributeChange.luau +46 -46
  59. package/vendor/fusion/Instances/AttributeOut.luau +63 -63
  60. package/vendor/fusion/Instances/Child.luau +21 -21
  61. package/vendor/fusion/Instances/Children.luau +147 -147
  62. package/vendor/fusion/Instances/Hydrate.luau +32 -32
  63. package/vendor/fusion/Instances/New.luau +52 -52
  64. package/vendor/fusion/Instances/OnChange.luau +49 -49
  65. package/vendor/fusion/Instances/OnEvent.luau +53 -53
  66. package/vendor/fusion/Instances/Out.luau +69 -69
  67. package/vendor/fusion/Instances/applyInstanceProps.luau +148 -148
  68. package/vendor/fusion/Instances/defaultProps.luau +194 -194
  69. package/vendor/fusion/LICENSE +21 -21
  70. package/vendor/fusion/Logging/formatError.luau +48 -48
  71. package/vendor/fusion/Logging/messages.luau +51 -51
  72. package/vendor/fusion/Logging/parseError.luau +24 -24
  73. package/vendor/fusion/Memory/checkLifetime.luau +133 -133
  74. package/vendor/fusion/Memory/deriveScope.luau +23 -23
  75. package/vendor/fusion/Memory/deriveScopeImpl.luau +44 -44
  76. package/vendor/fusion/Memory/doCleanup.luau +78 -78
  77. package/vendor/fusion/Memory/innerScope.luau +33 -33
  78. package/vendor/fusion/Memory/legacyCleanup.luau +17 -17
  79. package/vendor/fusion/Memory/needsDestruction.luau +16 -16
  80. package/vendor/fusion/Memory/poisonScope.luau +33 -33
  81. package/vendor/fusion/Memory/scopePool.luau +54 -54
  82. package/vendor/fusion/Memory/scoped.luau +26 -26
  83. package/vendor/fusion/Memory/whichLivesLonger.luau +74 -74
  84. package/vendor/fusion/RobloxExternal.luau +97 -97
  85. package/vendor/fusion/State/Computed.luau +138 -138
  86. package/vendor/fusion/State/For/Disassembly.luau +210 -210
  87. package/vendor/fusion/State/For/ForTypes.luau +30 -30
  88. package/vendor/fusion/State/For/init.luau +109 -109
  89. package/vendor/fusion/State/ForKeys.luau +93 -93
  90. package/vendor/fusion/State/ForPairs.luau +96 -96
  91. package/vendor/fusion/State/ForValues.luau +93 -93
  92. package/vendor/fusion/State/Value.luau +87 -87
  93. package/vendor/fusion/State/castToState.luau +25 -25
  94. package/vendor/fusion/State/peek.luau +30 -30
  95. package/vendor/fusion/Types.luau +314 -314
  96. package/vendor/fusion/Utility/Contextual.luau +90 -90
  97. package/vendor/fusion/Utility/Safe.luau +22 -22
  98. package/vendor/fusion/Utility/isSimilar.luau +29 -29
  99. package/vendor/fusion/Utility/merge.luau +35 -35
  100. package/vendor/fusion/Utility/nameOf.luau +34 -34
  101. package/vendor/fusion/Utility/never.luau +13 -13
  102. package/vendor/fusion/Utility/nicknames.luau +10 -10
  103. package/vendor/fusion/Utility/xtypeof.luau +26 -26
  104. package/vendor/fusion/init.luau +82 -82
  105. package/vendor/profilestore/init.luau +2242 -2242
  106. package/vendor/promise/init.luau +1982 -1982
  107. package/vendor/rbxutil/buffer-util/Buffer.test.luau +25 -25
  108. package/vendor/rbxutil/buffer-util/BufferReader.luau +228 -228
  109. package/vendor/rbxutil/buffer-util/BufferWriter.luau +269 -269
  110. package/vendor/rbxutil/buffer-util/DataTypeBuffer.luau +223 -223
  111. package/vendor/rbxutil/buffer-util/Types.luau +60 -60
  112. package/vendor/rbxutil/buffer-util/index.d.ts +153 -153
  113. package/vendor/rbxutil/buffer-util/init.luau +41 -41
  114. package/vendor/rbxutil/buffer-util/package.json +16 -16
  115. package/vendor/rbxutil/buffer-util/wally.toml +9 -9
  116. package/vendor/rbxutil/comm/Client/ClientComm.luau +232 -232
  117. package/vendor/rbxutil/comm/Client/ClientRemoteProperty.luau +156 -156
  118. package/vendor/rbxutil/comm/Client/ClientRemoteSignal.luau +109 -109
  119. package/vendor/rbxutil/comm/Client/init.luau +135 -135
  120. package/vendor/rbxutil/comm/Server/RemoteProperty.luau +295 -295
  121. package/vendor/rbxutil/comm/Server/RemoteSignal.luau +211 -211
  122. package/vendor/rbxutil/comm/Server/ServerComm.luau +211 -211
  123. package/vendor/rbxutil/comm/Server/init.luau +140 -140
  124. package/vendor/rbxutil/comm/Types.luau +18 -18
  125. package/vendor/rbxutil/comm/Util.luau +27 -27
  126. package/vendor/rbxutil/comm/init.luau +35 -35
  127. package/vendor/rbxutil/comm/wally.toml +13 -13
  128. package/vendor/rbxutil/component/init.luau +759 -759
  129. package/vendor/rbxutil/component/init.test.luau +311 -311
  130. package/vendor/rbxutil/component/wally.toml +14 -14
  131. package/vendor/rbxutil/concur/init.luau +542 -542
  132. package/vendor/rbxutil/concur/init.test.luau +364 -364
  133. package/vendor/rbxutil/concur/wally.toml +8 -8
  134. package/vendor/rbxutil/enum-list/init.luau +101 -101
  135. package/vendor/rbxutil/enum-list/init.test.luau +91 -91
  136. package/vendor/rbxutil/enum-list/wally.toml +8 -8
  137. package/vendor/rbxutil/find/index.d.ts +20 -20
  138. package/vendor/rbxutil/find/init.luau +44 -44
  139. package/vendor/rbxutil/find/package.json +17 -17
  140. package/vendor/rbxutil/find/wally.toml +8 -8
  141. package/vendor/rbxutil/input/Gamepad.luau +559 -559
  142. package/vendor/rbxutil/input/Keyboard.luau +124 -124
  143. package/vendor/rbxutil/input/Mouse.luau +278 -278
  144. package/vendor/rbxutil/input/PreferredInput.luau +91 -91
  145. package/vendor/rbxutil/input/Touch.luau +120 -120
  146. package/vendor/rbxutil/input/init.luau +33 -33
  147. package/vendor/rbxutil/input/wally.toml +12 -12
  148. package/vendor/rbxutil/loader/index.d.ts +15 -15
  149. package/vendor/rbxutil/loader/init.luau +137 -137
  150. package/vendor/rbxutil/loader/wally.toml +8 -8
  151. package/vendor/rbxutil/log/index.d.ts +38 -38
  152. package/vendor/rbxutil/log/init.luau +746 -746
  153. package/vendor/rbxutil/log/wally.toml +8 -8
  154. package/vendor/rbxutil/net/init.luau +190 -190
  155. package/vendor/rbxutil/net/wally.toml +8 -8
  156. package/vendor/rbxutil/option/index.d.ts +44 -44
  157. package/vendor/rbxutil/option/init.luau +489 -489
  158. package/vendor/rbxutil/option/init.test.luau +342 -342
  159. package/vendor/rbxutil/option/wally.toml +8 -8
  160. package/vendor/rbxutil/pid/index.d.ts +53 -53
  161. package/vendor/rbxutil/pid/init.luau +195 -195
  162. package/vendor/rbxutil/pid/package.json +16 -16
  163. package/vendor/rbxutil/pid/wally.toml +9 -9
  164. package/vendor/rbxutil/quaternion/index.d.ts +117 -117
  165. package/vendor/rbxutil/quaternion/init.luau +570 -570
  166. package/vendor/rbxutil/quaternion/package.json +16 -16
  167. package/vendor/rbxutil/quaternion/wally.toml +9 -9
  168. package/vendor/rbxutil/query/index.d.ts +43 -43
  169. package/vendor/rbxutil/query/init.luau +117 -117
  170. package/vendor/rbxutil/query/package.json +18 -18
  171. package/vendor/rbxutil/query/wally.toml +9 -9
  172. package/vendor/rbxutil/sequent/index.d.ts +28 -28
  173. package/vendor/rbxutil/sequent/init.luau +340 -340
  174. package/vendor/rbxutil/sequent/package.json +16 -16
  175. package/vendor/rbxutil/sequent/wally.toml +9 -9
  176. package/vendor/rbxutil/ser/init.luau +175 -175
  177. package/vendor/rbxutil/ser/init.test.luau +50 -50
  178. package/vendor/rbxutil/ser/wally.toml +11 -11
  179. package/vendor/rbxutil/shake/index.d.ts +36 -36
  180. package/vendor/rbxutil/shake/init.luau +532 -532
  181. package/vendor/rbxutil/shake/init.test.luau +267 -267
  182. package/vendor/rbxutil/shake/package.json +16 -16
  183. package/vendor/rbxutil/shake/wally.toml +9 -9
  184. package/vendor/rbxutil/signal/index.d.ts +100 -100
  185. package/vendor/rbxutil/signal/init.luau +432 -432
  186. package/vendor/rbxutil/signal/init.test.luau +190 -190
  187. package/vendor/rbxutil/signal/package.json +17 -17
  188. package/vendor/rbxutil/signal/wally.toml +9 -9
  189. package/vendor/rbxutil/silo/TableWatcher.luau +65 -65
  190. package/vendor/rbxutil/silo/Util.luau +55 -55
  191. package/vendor/rbxutil/silo/init.luau +338 -338
  192. package/vendor/rbxutil/silo/init.test.luau +215 -215
  193. package/vendor/rbxutil/silo/wally.toml +8 -8
  194. package/vendor/rbxutil/spring/index.d.ts +40 -40
  195. package/vendor/rbxutil/spring/init.luau +97 -97
  196. package/vendor/rbxutil/spring/package.json +17 -17
  197. package/vendor/rbxutil/spring/wally.toml +8 -8
  198. package/vendor/rbxutil/stream/index.d.ts +88 -88
  199. package/vendor/rbxutil/stream/init.luau +597 -597
  200. package/vendor/rbxutil/stream/package.json +18 -18
  201. package/vendor/rbxutil/stream/wally.toml +9 -9
  202. package/vendor/rbxutil/streamable/Streamable.luau +202 -202
  203. package/vendor/rbxutil/streamable/StreamableUtil.luau +80 -80
  204. package/vendor/rbxutil/streamable/init.luau +8 -8
  205. package/vendor/rbxutil/streamable/wally.toml +12 -12
  206. package/vendor/rbxutil/symbol/init.luau +56 -56
  207. package/vendor/rbxutil/symbol/init.test.luau +37 -37
  208. package/vendor/rbxutil/symbol/wally.toml +8 -8
  209. package/vendor/rbxutil/table-util/init.luau +938 -938
  210. package/vendor/rbxutil/table-util/init.test.luau +439 -439
  211. package/vendor/rbxutil/task-queue/index.d.ts +27 -27
  212. package/vendor/rbxutil/task-queue/init.luau +97 -97
  213. package/vendor/rbxutil/task-queue/wally.toml +8 -8
  214. package/vendor/rbxutil/timer/index.d.ts +81 -81
  215. package/vendor/rbxutil/timer/init.luau +249 -249
  216. package/vendor/rbxutil/timer/init.test.luau +73 -73
  217. package/vendor/rbxutil/timer/wally.toml +11 -11
  218. package/vendor/rbxutil/tree/index.d.ts +15 -15
  219. package/vendor/rbxutil/tree/init.luau +137 -137
  220. package/vendor/rbxutil/tree/wally.toml +8 -8
  221. package/vendor/rbxutil/trove/index.d.ts +46 -46
  222. package/vendor/rbxutil/trove/init.luau +787 -787
  223. package/vendor/rbxutil/trove/init.test.luau +203 -203
  224. package/vendor/rbxutil/trove/wally.toml +8 -8
  225. package/vendor/rbxutil/typed-remote/init.luau +196 -196
  226. package/vendor/rbxutil/typed-remote/wally.toml +8 -8
  227. package/vendor/rbxutil/wait-for/index.d.ts +17 -17
  228. package/vendor/rbxutil/wait-for/init.luau +257 -257
  229. package/vendor/rbxutil/wait-for/init.test.luau +182 -182
  230. package/vendor/rbxutil/wait-for/wally.toml +11 -11
  231. package/vendor/t/t.lua +1350 -1350
  232. package/vendor/testez/Context.lua +26 -26
  233. package/vendor/testez/Expectation.lua +311 -311
  234. package/vendor/testez/ExpectationContext.lua +38 -38
  235. package/vendor/testez/LifecycleHooks.lua +89 -89
  236. package/vendor/testez/Reporters/TeamCityReporter.lua +101 -101
  237. package/vendor/testez/Reporters/TextReporter.lua +105 -105
  238. package/vendor/testez/Reporters/TextReporterQuiet.lua +96 -96
  239. package/vendor/testez/TestBootstrap.lua +146 -146
  240. package/vendor/testez/TestEnum.lua +27 -27
  241. package/vendor/testez/TestPlan.lua +304 -304
  242. package/vendor/testez/TestPlanner.lua +39 -39
  243. package/vendor/testez/TestResults.lua +111 -111
  244. package/vendor/testez/TestRunner.lua +188 -188
  245. package/vendor/testez/TestSession.lua +243 -243
  246. package/vendor/testez/init.lua +39 -39
@@ -1,542 +1,542 @@
1
- --!strict
2
-
3
- -- Concur
4
- -- Stephen Leitnick
5
- -- May 01, 2022
6
-
7
- type Error = any
8
- type AnyFn = (...any) -> ...any
9
-
10
- --[=[
11
- @class Concur
12
-
13
- Concurrency class for helping run tasks concurrently. In other words, Concur allows
14
- developers to watch coroutines/threads. Completion status, returned values, and
15
- errors can all be tracked.
16
-
17
- For instance, Concur could be used to concurrently save all player data
18
- at the same time when the game closes down:
19
-
20
- ```lua
21
- game:BindToClose(function()
22
- local all = {}
23
- for _,player in Players:GetPlayers() do
24
- local save = Concur.spawn(function()
25
- DoSomethingToSaveData(player)
26
- end)
27
- table.insert(all, save)
28
- end
29
- local allConcur = Concur.all(all)
30
- allConcur:Await()
31
- end)
32
- ```
33
- ]=]
34
- local Concur = {}
35
- Concur.__index = Concur
36
-
37
- --[=[
38
- @within Concur
39
- @interface Errors
40
- .Stopped "Stopped"
41
- .Timeout "Timeout"
42
- ]=]
43
-
44
- --[=[
45
- @within Concur
46
- @readonly
47
- @prop Errors Errors
48
- ]=]
49
- Concur.Errors = {
50
- Stopped = "Stopped",
51
- Timeout = "Timeout",
52
- }
53
-
54
- function Concur._new(fn: AnyFn, spawner: AnyFn, ...: any): Concur
55
- local self: Concur = setmetatable({
56
- _completed = false,
57
- _res = nil,
58
- _err = nil,
59
- _awaitingThreads = {},
60
- _thread = nil,
61
- }, Concur)
62
-
63
- self._thread = spawner(function(...)
64
- local pcallRes = table.pack(pcall(fn, ...))
65
- self._completed = true
66
- self._err = if not pcallRes[1] then pcallRes[2] else nil
67
- if self._err ~= nil then
68
- for _, thread in ipairs(self._awaitingThreads) do
69
- task.spawn(thread, self._err)
70
- end
71
- else
72
- local res = table.move(pcallRes, 2, #pcallRes, 1, table.create(#pcallRes - 1))
73
- self._res = res
74
- for _, thread in ipairs(self._awaitingThreads) do
75
- task.spawn(thread, nil, table.unpack(res, 1, res.n))
76
- end
77
- end
78
- end, ...)
79
-
80
- return self
81
- end
82
-
83
- --[=[
84
- Spawns the function using `task.spawn`.
85
-
86
- ```lua
87
- local c = Concur.spawn(function()
88
- task.wait(5)
89
- return "Hello!"
90
- end)
91
-
92
- c:OnCompleted(function(err, msg)
93
- if err then
94
- error(err)
95
- end
96
- print(msg) --> Hello!
97
- end))
98
- ```
99
- ]=]
100
- function Concur.spawn(fn: AnyFn, ...: any): Concur
101
- if type(fn) ~= "function" then
102
- error("Concur.spawn argument must be a function; got " .. type(fn), 2)
103
- end
104
- return Concur._new(fn, task.spawn, ...)
105
- end
106
-
107
- --[=[
108
- Same as `Concur.spawn`, but uses `task.defer` internally.
109
- ]=]
110
- function Concur.defer(fn: AnyFn, ...: any): Concur
111
- if type(fn) ~= "function" then
112
- error("Concur.defer argument must be a function; got " .. type(fn), 2)
113
- end
114
- return Concur._new(fn, task.defer, ...)
115
- end
116
-
117
- --[=[
118
- Same as `Concur.spawn`, but uses `task.delay` internally.
119
- ]=]
120
- function Concur.delay(delayTime: number, fn: AnyFn, ...: any): Concur
121
- if type(fn) ~= "function" then
122
- error("Concur.delay argument must be a function; got " .. type(fn), 2)
123
- end
124
- return Concur._new(fn, function(...)
125
- return task.delay(delayTime, ...)
126
- end, ...)
127
- end
128
-
129
- --[=[
130
- Resolves to the given value right away.
131
-
132
- ```lua
133
- local val = Concur.value(10)
134
- val:OnCompleted(function(v)
135
- print(v) --> 10
136
- end)
137
- ```
138
- ]=]
139
- function Concur.value(value: any): Concur
140
- return Concur.spawn(function()
141
- return value
142
- end)
143
- end
144
-
145
- --[=[
146
- Completes the Concur instance once the event is fired and the predicate
147
- function returns `true` (if no predicate is given, then completes once
148
- the event first fires).
149
-
150
- The Concur instance will return the values given by the event.
151
-
152
- ```lua
153
- -- Wait for next player to touch an object:
154
- local touch = Concur.event(part.Touched, function(toucher)
155
- return Players:GetPlayerFromCharacter(toucher.Parent) ~= nil
156
- end)
157
-
158
- touch:OnCompleted(function(err, toucher)
159
- print(toucher)
160
- end)
161
- ```
162
- ]=]
163
- function Concur.event(event: RBXScriptSignal, predicate: ((...any) -> boolean)?)
164
- local connection, thread
165
-
166
- connection = event:Connect(function(...)
167
- if not thread then
168
- return
169
- end
170
- if predicate == nil or predicate(...) then
171
- connection:Disconnect()
172
- task.spawn(thread, ...)
173
- end
174
- end)
175
-
176
- local c = Concur.spawn(function()
177
- thread = coroutine.running()
178
- return coroutine.yield()
179
- end)
180
-
181
- c:OnCompleted(function(err)
182
- connection:Disconnect()
183
- if coroutine.status(thread) == "suspended" then
184
- task.spawn(thread, err)
185
- end
186
- end)
187
-
188
- return c
189
- end
190
-
191
- --[=[
192
- Completes once _all_ Concur instances have been completed. All values
193
- will be available in a packed table in the same order they were passed.
194
-
195
- ```lua
196
- local c1 = Concur.spawn(function()
197
- return 10
198
- end)
199
-
200
- local c2 = Concur.delay(0.5, function()
201
- return 15
202
- end)
203
-
204
- local c3 = Concur.value(20)
205
-
206
- local c4 = Concur.spawn(function()
207
- error("failed")
208
- end)
209
-
210
- Concur.all({c1, c2, c3}):OnCompleted(function(err, values)
211
- print(values) --> {{nil, 10}, {nil, 15}, {nil, 20}, {"failed", nil}}
212
- end)
213
- ```
214
- ]=]
215
- function Concur.all(concurs: { Concur }): Concur
216
- if #concurs == 0 then
217
- return Concur.value(nil)
218
- end
219
-
220
- return Concur.spawn(function()
221
- local numCompleted = 0
222
- local total = #concurs
223
- local thread = coroutine.running()
224
- local allRes = table.create(total)
225
- for i, concur in ipairs(concurs) do
226
- concur:OnCompleted(function(...)
227
- allRes[i] = table.pack(...)
228
- numCompleted += 1
229
- if numCompleted >= total and coroutine.status(thread) == "suspended" then
230
- task.spawn(thread)
231
- end
232
- end)
233
- end
234
- if numCompleted < total then
235
- coroutine.yield()
236
- end
237
- return allRes
238
- end)
239
- end
240
-
241
- --[=[
242
- Completes once the first Concur instance is completed _without an error_. All other Concur
243
- instances are then stopped.
244
-
245
- ```lua
246
- local c1 = Concur.delay(1, function()
247
- return 10
248
- end)
249
-
250
- local c2 = Concur.delay(0.5, function()
251
- return 5
252
- end)
253
-
254
- Concur.first({c1, c2}):OnCompleted(function(err, num)
255
- print(num) --> 5
256
- end)
257
- ```
258
- ]=]
259
- function Concur.first(concurs: { Concur }): Concur
260
- if #concurs == 0 then
261
- return Concur.value(nil)
262
- end
263
-
264
- return Concur.spawn(function()
265
- local thread = coroutine.running()
266
- local res = nil
267
- local firstConcur = nil
268
- for _, concur in ipairs(concurs) do
269
- concur:OnCompleted(function(err, ...)
270
- if res or err ~= nil then
271
- return
272
- end
273
- firstConcur = concur
274
- res = table.pack(...)
275
- if coroutine.status(thread) == "suspended" then
276
- task.spawn(thread)
277
- end
278
- end)
279
- end
280
- if res == nil then
281
- coroutine.yield()
282
- end
283
- for _, concur in ipairs(concurs) do
284
- if concur == firstConcur then
285
- continue
286
- end
287
- concur:Stop()
288
- end
289
- return table.unpack(res, 1, res.n)
290
- end)
291
- end
292
-
293
- --[=[
294
- Stops the Concur instance. The underlying thread will be cancelled using
295
- `task.cancel`. Any bound `OnCompleted` functions or threads waiting with
296
- `Await` will be completed with the error `Concur.Errors.Stopped`.
297
-
298
- ```lua
299
- local c = Concur.spawn(function()
300
- for i = 1,10 do
301
- print(i)
302
- task.wait(1)
303
- end
304
- end)
305
-
306
- task.wait(2.5)
307
- c:Stop() -- At this point, will have only printed 1 and 2
308
- ```
309
- ]=]
310
- function Concur:Stop()
311
- if self._completed then
312
- return
313
- end
314
- self._completed = true
315
- self._err = Concur.Errors.Stopped
316
- task.cancel(self._thread)
317
- for _, thread: thread in ipairs(self._awaitingThreads) do
318
- task.spawn(thread, Concur.Errors.Stopped)
319
- end
320
- end
321
-
322
- --[=[
323
- Check if the Concur instance is finished.
324
- ]=]
325
- function Concur:IsCompleted(): boolean
326
- return self._completed
327
- end
328
-
329
- --[=[
330
- @yields
331
- Yields the calling thread until the Concur instance is completed:
332
-
333
- ```lua
334
- local c = Concur.delay(5, function()
335
- return "Hi"
336
- end)
337
-
338
- local err, msg = c:Await()
339
- print(msg) --> Hi
340
- ```
341
-
342
- The `Await` method can be called _after_ the Concur instance
343
- has been completed too, in which case the completed values
344
- will be returned immediately without yielding the thread:
345
-
346
- ```lua
347
- local c = Concur.spawn(function()
348
- return 10
349
- end)
350
-
351
- task.wait(5)
352
- -- Called after 'c' has been completed, but still captures the value:
353
- local err, num = c:Await()
354
- print(num) --> 10
355
- ```
356
-
357
- It is always good practice to make sure that the `err` value is handled
358
- by checking if it is not nil:
359
-
360
- ```lua
361
- local c = Concur.spawn(function()
362
- error("failed")
363
- end)
364
-
365
- local err, value = c:Await()
366
-
367
- if err ~= nil then
368
- print(err) --> failed
369
- -- Handle error `err`
370
- else
371
- -- Handle `value`
372
- end
373
- ```
374
-
375
- This will stop awaiting if the Concur instance was stopped
376
- too, in which case the `err` will be equal to
377
- `Concur.Errors.Stopped`:
378
-
379
- ```lua
380
- local c = Concur.delay(10, function() end)
381
- c:Stop()
382
- local err = c:Await()
383
- if err == Concur.Errors.Stopped then
384
- print("Was stopped")
385
- end
386
- ```
387
-
388
- An optional timeout can be given, which will return the
389
- `Concur.Errors.Timeout` error if timed out. Timing out
390
- does _not_ stop the Concur instance, so other callers
391
- to `Await` or `OnCompleted` can still grab the resulting
392
- values.
393
-
394
- ```lua
395
- local c = Concur.delay(10, function() end)
396
- local err = c:Await(1)
397
- if err == Concur.Errors.Timeout then
398
- -- Handle timeout
399
- end
400
- ```
401
- ]=]
402
- function Concur:Await(timeout: number?): (Error, ...any?)
403
- if self._completed then
404
- if self._err ~= nil then
405
- return self._err
406
- else
407
- return nil, if self._res == nil then nil else table.unpack(self._res, 1, self._res.n)
408
- end
409
- end
410
-
411
- local thread = coroutine.running()
412
- table.insert(self._awaitingThreads, thread)
413
-
414
- if timeout then
415
- local delayThread = task.delay(timeout, function()
416
- local index = table.find(self._awaitingThreads, thread)
417
- if index then
418
- table.remove(self._awaitingThreads, index)
419
- task.spawn(thread, Concur.Errors.Timeout)
420
- end
421
- end)
422
- local res = table.pack(coroutine.yield())
423
- if coroutine.status(delayThread) ~= "normal" then
424
- task.cancel(delayThread)
425
- end
426
- return table.unpack(res, 1, res.n)
427
- else
428
- return coroutine.yield()
429
- end
430
- end
431
-
432
- --[=[
433
- Calls the given function once the Concur instance is completed:
434
-
435
- ```lua
436
- local c = Concur.delay(5, function()
437
- return "Hi"
438
- end)
439
-
440
- c:OnCompleted(function(err, msg)
441
- print(msg) --> Hi
442
- end)
443
- ```
444
-
445
- A function is returned that can be used to unbind the function to
446
- no longer fire when the Concur instance is completed:
447
-
448
- ```lua
449
- local c = Concur.delay(5, function() end)
450
- local unbind = c:OnCompleted(function()
451
- print("Completed")
452
- end)
453
- unbind()
454
- -- Never prints "Completed"
455
- ```
456
-
457
- The `OnCompleted` method can be called _after_ the Concur instance
458
- has been completed too, in which case the given function will be
459
- called immediately with the completed values:
460
-
461
- ```lua
462
- local c = Concur.spawn(function()
463
- return 10
464
- end)
465
-
466
- task.wait(5)
467
- -- Called after 'c' has been completed, but still captures the value:
468
- c:OnCompleted(function(err, num)
469
- print(num) --> 10
470
- end)
471
- ```
472
-
473
- It is always good practice to make sure that the `err` value is handled
474
- by checking if it is not nil:
475
-
476
- ```lua
477
- local c = Concur.spawn(function()
478
- error("failed")
479
- end)
480
-
481
- c:OnCompleted(function(err, value)
482
- if err ~= nil then
483
- print(err) --> failed
484
- -- Handle error `err`
485
- return
486
- end
487
- -- Handle `value`
488
- end)
489
- ```
490
-
491
- This will call the function if the Concur instance was stopped
492
- too, in which case the `err` will be equal to
493
- `Concur.Errors.Stopped`:
494
-
495
- ```lua
496
- local c = Concur.delay(10, function() end)
497
- c:OnCompleted(function(err)
498
- if err == Concur.Errors.Stopped then
499
- print("Was stopped")
500
- end
501
- end)
502
- c:Stop()
503
- ```
504
-
505
- An optional timeout can also be supplied, which will call the
506
- function with the `Concur.Errors.Timeout` error:
507
-
508
- ```lua
509
- local c = Concur.delay(10, function() end)
510
- c:OnCompleted(function(err)
511
- if err == Concur.Errors.Timeout then
512
- -- Handle timeout
513
- end
514
- end, 1)
515
- ```
516
- ]=]
517
- function Concur:OnCompleted(fn: (Error, ...any?) -> (), timeout: number?): () -> ()
518
- local thread = task.spawn(function()
519
- fn(self:Await(timeout))
520
- end)
521
-
522
- -- Unbind:
523
- return function()
524
- task.cancel(thread)
525
- local index = table.find(self._awaitingThreads, thread)
526
- if index then
527
- table.remove(self._awaitingThreads, index)
528
- end
529
- end
530
- end
531
-
532
- type ConcurObj = {
533
- _completed: boolean,
534
- _res: { any }?,
535
- _err: string?,
536
- _awaitingThreads: { thread },
537
- _thread: thread?,
538
- }
539
-
540
- export type Concur = typeof(setmetatable({} :: ConcurObj, Concur))
541
-
542
- return Concur
1
+ --!strict
2
+
3
+ -- Concur
4
+ -- Stephen Leitnick
5
+ -- May 01, 2022
6
+
7
+ type Error = any
8
+ type AnyFn = (...any) -> ...any
9
+
10
+ --[=[
11
+ @class Concur
12
+
13
+ Concurrency class for helping run tasks concurrently. In other words, Concur allows
14
+ developers to watch coroutines/threads. Completion status, returned values, and
15
+ errors can all be tracked.
16
+
17
+ For instance, Concur could be used to concurrently save all player data
18
+ at the same time when the game closes down:
19
+
20
+ ```lua
21
+ game:BindToClose(function()
22
+ local all = {}
23
+ for _,player in Players:GetPlayers() do
24
+ local save = Concur.spawn(function()
25
+ DoSomethingToSaveData(player)
26
+ end)
27
+ table.insert(all, save)
28
+ end
29
+ local allConcur = Concur.all(all)
30
+ allConcur:Await()
31
+ end)
32
+ ```
33
+ ]=]
34
+ local Concur = {}
35
+ Concur.__index = Concur
36
+
37
+ --[=[
38
+ @within Concur
39
+ @interface Errors
40
+ .Stopped "Stopped"
41
+ .Timeout "Timeout"
42
+ ]=]
43
+
44
+ --[=[
45
+ @within Concur
46
+ @readonly
47
+ @prop Errors Errors
48
+ ]=]
49
+ Concur.Errors = {
50
+ Stopped = "Stopped",
51
+ Timeout = "Timeout",
52
+ }
53
+
54
+ function Concur._new(fn: AnyFn, spawner: AnyFn, ...: any): Concur
55
+ local self: Concur = setmetatable({
56
+ _completed = false,
57
+ _res = nil,
58
+ _err = nil,
59
+ _awaitingThreads = {},
60
+ _thread = nil,
61
+ }, Concur)
62
+
63
+ self._thread = spawner(function(...)
64
+ local pcallRes = table.pack(pcall(fn, ...))
65
+ self._completed = true
66
+ self._err = if not pcallRes[1] then pcallRes[2] else nil
67
+ if self._err ~= nil then
68
+ for _, thread in ipairs(self._awaitingThreads) do
69
+ task.spawn(thread, self._err)
70
+ end
71
+ else
72
+ local res = table.move(pcallRes, 2, #pcallRes, 1, table.create(#pcallRes - 1))
73
+ self._res = res
74
+ for _, thread in ipairs(self._awaitingThreads) do
75
+ task.spawn(thread, nil, table.unpack(res, 1, res.n))
76
+ end
77
+ end
78
+ end, ...)
79
+
80
+ return self
81
+ end
82
+
83
+ --[=[
84
+ Spawns the function using `task.spawn`.
85
+
86
+ ```lua
87
+ local c = Concur.spawn(function()
88
+ task.wait(5)
89
+ return "Hello!"
90
+ end)
91
+
92
+ c:OnCompleted(function(err, msg)
93
+ if err then
94
+ error(err)
95
+ end
96
+ print(msg) --> Hello!
97
+ end))
98
+ ```
99
+ ]=]
100
+ function Concur.spawn(fn: AnyFn, ...: any): Concur
101
+ if type(fn) ~= "function" then
102
+ error("Concur.spawn argument must be a function; got " .. type(fn), 2)
103
+ end
104
+ return Concur._new(fn, task.spawn, ...)
105
+ end
106
+
107
+ --[=[
108
+ Same as `Concur.spawn`, but uses `task.defer` internally.
109
+ ]=]
110
+ function Concur.defer(fn: AnyFn, ...: any): Concur
111
+ if type(fn) ~= "function" then
112
+ error("Concur.defer argument must be a function; got " .. type(fn), 2)
113
+ end
114
+ return Concur._new(fn, task.defer, ...)
115
+ end
116
+
117
+ --[=[
118
+ Same as `Concur.spawn`, but uses `task.delay` internally.
119
+ ]=]
120
+ function Concur.delay(delayTime: number, fn: AnyFn, ...: any): Concur
121
+ if type(fn) ~= "function" then
122
+ error("Concur.delay argument must be a function; got " .. type(fn), 2)
123
+ end
124
+ return Concur._new(fn, function(...)
125
+ return task.delay(delayTime, ...)
126
+ end, ...)
127
+ end
128
+
129
+ --[=[
130
+ Resolves to the given value right away.
131
+
132
+ ```lua
133
+ local val = Concur.value(10)
134
+ val:OnCompleted(function(v)
135
+ print(v) --> 10
136
+ end)
137
+ ```
138
+ ]=]
139
+ function Concur.value(value: any): Concur
140
+ return Concur.spawn(function()
141
+ return value
142
+ end)
143
+ end
144
+
145
+ --[=[
146
+ Completes the Concur instance once the event is fired and the predicate
147
+ function returns `true` (if no predicate is given, then completes once
148
+ the event first fires).
149
+
150
+ The Concur instance will return the values given by the event.
151
+
152
+ ```lua
153
+ -- Wait for next player to touch an object:
154
+ local touch = Concur.event(part.Touched, function(toucher)
155
+ return Players:GetPlayerFromCharacter(toucher.Parent) ~= nil
156
+ end)
157
+
158
+ touch:OnCompleted(function(err, toucher)
159
+ print(toucher)
160
+ end)
161
+ ```
162
+ ]=]
163
+ function Concur.event(event: RBXScriptSignal, predicate: ((...any) -> boolean)?)
164
+ local connection, thread
165
+
166
+ connection = event:Connect(function(...)
167
+ if not thread then
168
+ return
169
+ end
170
+ if predicate == nil or predicate(...) then
171
+ connection:Disconnect()
172
+ task.spawn(thread, ...)
173
+ end
174
+ end)
175
+
176
+ local c = Concur.spawn(function()
177
+ thread = coroutine.running()
178
+ return coroutine.yield()
179
+ end)
180
+
181
+ c:OnCompleted(function(err)
182
+ connection:Disconnect()
183
+ if coroutine.status(thread) == "suspended" then
184
+ task.spawn(thread, err)
185
+ end
186
+ end)
187
+
188
+ return c
189
+ end
190
+
191
+ --[=[
192
+ Completes once _all_ Concur instances have been completed. All values
193
+ will be available in a packed table in the same order they were passed.
194
+
195
+ ```lua
196
+ local c1 = Concur.spawn(function()
197
+ return 10
198
+ end)
199
+
200
+ local c2 = Concur.delay(0.5, function()
201
+ return 15
202
+ end)
203
+
204
+ local c3 = Concur.value(20)
205
+
206
+ local c4 = Concur.spawn(function()
207
+ error("failed")
208
+ end)
209
+
210
+ Concur.all({c1, c2, c3}):OnCompleted(function(err, values)
211
+ print(values) --> {{nil, 10}, {nil, 15}, {nil, 20}, {"failed", nil}}
212
+ end)
213
+ ```
214
+ ]=]
215
+ function Concur.all(concurs: { Concur }): Concur
216
+ if #concurs == 0 then
217
+ return Concur.value(nil)
218
+ end
219
+
220
+ return Concur.spawn(function()
221
+ local numCompleted = 0
222
+ local total = #concurs
223
+ local thread = coroutine.running()
224
+ local allRes = table.create(total)
225
+ for i, concur in ipairs(concurs) do
226
+ concur:OnCompleted(function(...)
227
+ allRes[i] = table.pack(...)
228
+ numCompleted += 1
229
+ if numCompleted >= total and coroutine.status(thread) == "suspended" then
230
+ task.spawn(thread)
231
+ end
232
+ end)
233
+ end
234
+ if numCompleted < total then
235
+ coroutine.yield()
236
+ end
237
+ return allRes
238
+ end)
239
+ end
240
+
241
+ --[=[
242
+ Completes once the first Concur instance is completed _without an error_. All other Concur
243
+ instances are then stopped.
244
+
245
+ ```lua
246
+ local c1 = Concur.delay(1, function()
247
+ return 10
248
+ end)
249
+
250
+ local c2 = Concur.delay(0.5, function()
251
+ return 5
252
+ end)
253
+
254
+ Concur.first({c1, c2}):OnCompleted(function(err, num)
255
+ print(num) --> 5
256
+ end)
257
+ ```
258
+ ]=]
259
+ function Concur.first(concurs: { Concur }): Concur
260
+ if #concurs == 0 then
261
+ return Concur.value(nil)
262
+ end
263
+
264
+ return Concur.spawn(function()
265
+ local thread = coroutine.running()
266
+ local res = nil
267
+ local firstConcur = nil
268
+ for _, concur in ipairs(concurs) do
269
+ concur:OnCompleted(function(err, ...)
270
+ if res or err ~= nil then
271
+ return
272
+ end
273
+ firstConcur = concur
274
+ res = table.pack(...)
275
+ if coroutine.status(thread) == "suspended" then
276
+ task.spawn(thread)
277
+ end
278
+ end)
279
+ end
280
+ if res == nil then
281
+ coroutine.yield()
282
+ end
283
+ for _, concur in ipairs(concurs) do
284
+ if concur == firstConcur then
285
+ continue
286
+ end
287
+ concur:Stop()
288
+ end
289
+ return table.unpack(res, 1, res.n)
290
+ end)
291
+ end
292
+
293
+ --[=[
294
+ Stops the Concur instance. The underlying thread will be cancelled using
295
+ `task.cancel`. Any bound `OnCompleted` functions or threads waiting with
296
+ `Await` will be completed with the error `Concur.Errors.Stopped`.
297
+
298
+ ```lua
299
+ local c = Concur.spawn(function()
300
+ for i = 1,10 do
301
+ print(i)
302
+ task.wait(1)
303
+ end
304
+ end)
305
+
306
+ task.wait(2.5)
307
+ c:Stop() -- At this point, will have only printed 1 and 2
308
+ ```
309
+ ]=]
310
+ function Concur:Stop()
311
+ if self._completed then
312
+ return
313
+ end
314
+ self._completed = true
315
+ self._err = Concur.Errors.Stopped
316
+ task.cancel(self._thread)
317
+ for _, thread: thread in ipairs(self._awaitingThreads) do
318
+ task.spawn(thread, Concur.Errors.Stopped)
319
+ end
320
+ end
321
+
322
+ --[=[
323
+ Check if the Concur instance is finished.
324
+ ]=]
325
+ function Concur:IsCompleted(): boolean
326
+ return self._completed
327
+ end
328
+
329
+ --[=[
330
+ @yields
331
+ Yields the calling thread until the Concur instance is completed:
332
+
333
+ ```lua
334
+ local c = Concur.delay(5, function()
335
+ return "Hi"
336
+ end)
337
+
338
+ local err, msg = c:Await()
339
+ print(msg) --> Hi
340
+ ```
341
+
342
+ The `Await` method can be called _after_ the Concur instance
343
+ has been completed too, in which case the completed values
344
+ will be returned immediately without yielding the thread:
345
+
346
+ ```lua
347
+ local c = Concur.spawn(function()
348
+ return 10
349
+ end)
350
+
351
+ task.wait(5)
352
+ -- Called after 'c' has been completed, but still captures the value:
353
+ local err, num = c:Await()
354
+ print(num) --> 10
355
+ ```
356
+
357
+ It is always good practice to make sure that the `err` value is handled
358
+ by checking if it is not nil:
359
+
360
+ ```lua
361
+ local c = Concur.spawn(function()
362
+ error("failed")
363
+ end)
364
+
365
+ local err, value = c:Await()
366
+
367
+ if err ~= nil then
368
+ print(err) --> failed
369
+ -- Handle error `err`
370
+ else
371
+ -- Handle `value`
372
+ end
373
+ ```
374
+
375
+ This will stop awaiting if the Concur instance was stopped
376
+ too, in which case the `err` will be equal to
377
+ `Concur.Errors.Stopped`:
378
+
379
+ ```lua
380
+ local c = Concur.delay(10, function() end)
381
+ c:Stop()
382
+ local err = c:Await()
383
+ if err == Concur.Errors.Stopped then
384
+ print("Was stopped")
385
+ end
386
+ ```
387
+
388
+ An optional timeout can be given, which will return the
389
+ `Concur.Errors.Timeout` error if timed out. Timing out
390
+ does _not_ stop the Concur instance, so other callers
391
+ to `Await` or `OnCompleted` can still grab the resulting
392
+ values.
393
+
394
+ ```lua
395
+ local c = Concur.delay(10, function() end)
396
+ local err = c:Await(1)
397
+ if err == Concur.Errors.Timeout then
398
+ -- Handle timeout
399
+ end
400
+ ```
401
+ ]=]
402
+ function Concur:Await(timeout: number?): (Error, ...any?)
403
+ if self._completed then
404
+ if self._err ~= nil then
405
+ return self._err
406
+ else
407
+ return nil, if self._res == nil then nil else table.unpack(self._res, 1, self._res.n)
408
+ end
409
+ end
410
+
411
+ local thread = coroutine.running()
412
+ table.insert(self._awaitingThreads, thread)
413
+
414
+ if timeout then
415
+ local delayThread = task.delay(timeout, function()
416
+ local index = table.find(self._awaitingThreads, thread)
417
+ if index then
418
+ table.remove(self._awaitingThreads, index)
419
+ task.spawn(thread, Concur.Errors.Timeout)
420
+ end
421
+ end)
422
+ local res = table.pack(coroutine.yield())
423
+ if coroutine.status(delayThread) ~= "normal" then
424
+ task.cancel(delayThread)
425
+ end
426
+ return table.unpack(res, 1, res.n)
427
+ else
428
+ return coroutine.yield()
429
+ end
430
+ end
431
+
432
+ --[=[
433
+ Calls the given function once the Concur instance is completed:
434
+
435
+ ```lua
436
+ local c = Concur.delay(5, function()
437
+ return "Hi"
438
+ end)
439
+
440
+ c:OnCompleted(function(err, msg)
441
+ print(msg) --> Hi
442
+ end)
443
+ ```
444
+
445
+ A function is returned that can be used to unbind the function to
446
+ no longer fire when the Concur instance is completed:
447
+
448
+ ```lua
449
+ local c = Concur.delay(5, function() end)
450
+ local unbind = c:OnCompleted(function()
451
+ print("Completed")
452
+ end)
453
+ unbind()
454
+ -- Never prints "Completed"
455
+ ```
456
+
457
+ The `OnCompleted` method can be called _after_ the Concur instance
458
+ has been completed too, in which case the given function will be
459
+ called immediately with the completed values:
460
+
461
+ ```lua
462
+ local c = Concur.spawn(function()
463
+ return 10
464
+ end)
465
+
466
+ task.wait(5)
467
+ -- Called after 'c' has been completed, but still captures the value:
468
+ c:OnCompleted(function(err, num)
469
+ print(num) --> 10
470
+ end)
471
+ ```
472
+
473
+ It is always good practice to make sure that the `err` value is handled
474
+ by checking if it is not nil:
475
+
476
+ ```lua
477
+ local c = Concur.spawn(function()
478
+ error("failed")
479
+ end)
480
+
481
+ c:OnCompleted(function(err, value)
482
+ if err ~= nil then
483
+ print(err) --> failed
484
+ -- Handle error `err`
485
+ return
486
+ end
487
+ -- Handle `value`
488
+ end)
489
+ ```
490
+
491
+ This will call the function if the Concur instance was stopped
492
+ too, in which case the `err` will be equal to
493
+ `Concur.Errors.Stopped`:
494
+
495
+ ```lua
496
+ local c = Concur.delay(10, function() end)
497
+ c:OnCompleted(function(err)
498
+ if err == Concur.Errors.Stopped then
499
+ print("Was stopped")
500
+ end
501
+ end)
502
+ c:Stop()
503
+ ```
504
+
505
+ An optional timeout can also be supplied, which will call the
506
+ function with the `Concur.Errors.Timeout` error:
507
+
508
+ ```lua
509
+ local c = Concur.delay(10, function() end)
510
+ c:OnCompleted(function(err)
511
+ if err == Concur.Errors.Timeout then
512
+ -- Handle timeout
513
+ end
514
+ end, 1)
515
+ ```
516
+ ]=]
517
+ function Concur:OnCompleted(fn: (Error, ...any?) -> (), timeout: number?): () -> ()
518
+ local thread = task.spawn(function()
519
+ fn(self:Await(timeout))
520
+ end)
521
+
522
+ -- Unbind:
523
+ return function()
524
+ task.cancel(thread)
525
+ local index = table.find(self._awaitingThreads, thread)
526
+ if index then
527
+ table.remove(self._awaitingThreads, index)
528
+ end
529
+ end
530
+ end
531
+
532
+ type ConcurObj = {
533
+ _completed: boolean,
534
+ _res: { any }?,
535
+ _err: string?,
536
+ _awaitingThreads: { thread },
537
+ _thread: thread?,
538
+ }
539
+
540
+ export type Concur = typeof(setmetatable({} :: ConcurObj, Concur))
541
+
542
+ return Concur