cluaupp 1.4.1 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (340) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +10 -5
  3. package/api/CHANGELOG.md +3 -0
  4. package/api/chat.js +74 -0
  5. package/api/fixtures/roblox/README.md +6 -0
  6. package/api/fixtures/roblox/aux/sample.d.luau +6 -0
  7. package/api/fixtures/roblox/mini/Mini-API-Dump.json +72 -0
  8. package/api/generated/bench-latest.json +39 -0
  9. package/api/generated/capability-profile.json +76 -0
  10. package/api/generated/lsp-index.json +1 -0
  11. package/api/generated/luau/classes.d.luau +11221 -0
  12. package/api/generated/luau/datatypes.d.luau +327 -0
  13. package/api/generated/luau/enums.d.luau +4918 -0
  14. package/api/generated/luau/globals.d.luau +12 -0
  15. package/api/manifests/roblox-target.manifest.json +22 -0
  16. package/api/normalized/roblox-target.profile.json +237461 -0
  17. package/api/overrides/classes.json +8 -0
  18. package/api/overrides/compatibility.json +9 -0
  19. package/api/overrides/datatypes.json +1579 -0
  20. package/api/overrides/enums.json +8 -0
  21. package/api/overrides/globals.json +8 -0
  22. package/api/overrides/type-mappings.json +9 -0
  23. package/api/roblox-api.lock.json +9 -0
  24. package/data/Mini-API-Dump.json +1 -0
  25. package/editors/vscode/README.md +19 -0
  26. package/editors/vscode/extension.js +222 -21
  27. package/editors/vscode/icons/axiom.svg +4 -0
  28. package/editors/vscode/icons/bloom.svg +5 -0
  29. package/editors/vscode/icons/flare.svg +5 -0
  30. package/editors/vscode/icons/helm.svg +4 -0
  31. package/editors/vscode/icons/hive.svg +5 -0
  32. package/editors/vscode/icons/mint.svg +4 -0
  33. package/editors/vscode/icons/shift.svg +6 -0
  34. package/editors/vscode/language-configuration-schema.json +24 -0
  35. package/editors/vscode/language-configuration.json +21 -0
  36. package/editors/vscode/package.json +220 -6
  37. package/editors/vscode/snippets/axiom.json +12 -0
  38. package/editors/vscode/snippets/bloom.json +12 -0
  39. package/editors/vscode/snippets/flare.json +27 -0
  40. package/editors/vscode/snippets/helm.json +12 -0
  41. package/editors/vscode/snippets/hive.json +12 -0
  42. package/editors/vscode/snippets/mint.json +12 -0
  43. package/editors/vscode/snippets/shift.json +17 -0
  44. package/editors/vscode/syntaxes/axiom.tmLanguage.json +5 -0
  45. package/editors/vscode/syntaxes/bloom.tmLanguage.json +5 -0
  46. package/editors/vscode/syntaxes/flare.tmLanguage.json +75 -0
  47. package/editors/vscode/syntaxes/helm.tmLanguage.json +5 -0
  48. package/editors/vscode/syntaxes/hive.tmLanguage.json +5 -0
  49. package/editors/vscode/syntaxes/mint.tmLanguage.json +5 -0
  50. package/editors/vscode/syntaxes/schema.tmLanguage.json +89 -0
  51. package/editors/vscode/syntaxes/shift.tmLanguage.json +5 -0
  52. package/examples/game/.cluaupp/compile-cache/ReplicatedStorage__Shared__Constants__Datas__TemplateData.clh.json +1 -0
  53. package/examples/game/.cluaupp/compile-cache/ServerScriptService__Boot__DataBoot.server.clpp.json +1 -0
  54. package/examples/game/.cluaupp/compile-cache/ServerScriptService__Configuration__ServerConfig.clp.json +1 -0
  55. package/examples/game/.cluaupp/compile-cache/ServerScriptService__Handlers__Guard.server.clpp.json +1 -0
  56. package/examples/game/.cluaupp/compile-cache/ServerScriptService__Handlers__PlayerHandler.server.clpp.json +1 -0
  57. package/examples/game/.cluaupp/compile-cache/StarterPlayer__StarterCharacterScripts__Character__CharacterReady.client.clpp.json +1 -0
  58. package/examples/game/.cluaupp/compile-cache/StarterPlayer__StarterPlayerScripts__Controllers__DataController.client.clpp.json +1 -0
  59. package/examples/game/.cluaupp/compile-cache/index.json +12 -0
  60. package/examples/game/.cluaupp/datamodel-profile.json +94 -0
  61. package/examples/game/.vscode/extensions.json +3 -1
  62. package/examples/game/.vscode/settings.json +13 -1
  63. package/examples/game/include/clpp/datatypes.clh +337 -373
  64. package/examples/game/include/clpp/generated/datamodel.clh +25 -0
  65. package/examples/game/include/clpp/generated/enums.clh +1 -1
  66. package/examples/game/include/clpp/generated/instances.clh +41135 -41064
  67. package/examples/game/include/clpp/libs/axiom.clh +1 -1
  68. package/examples/game/include/clpp/libs/math.clh +14 -0
  69. package/examples/game/include/clpp/libs/roster.clh +6 -0
  70. package/examples/game/include/clpp/libs/spring.clh +17 -0
  71. package/examples/game/include/clpp/libs/ward.clh +1 -0
  72. package/examples/game/include/clpp/roblox.clh +3 -4
  73. package/examples/game/libs/ArrayIndexer/init.luau +209 -0
  74. package/examples/game/libs/Axiom/init.luau +608 -0
  75. package/examples/game/libs/Bloom/init.luau +433 -0
  76. package/examples/game/libs/Coil/init.luau +215 -0
  77. package/examples/game/libs/Crest/init.luau +426 -0
  78. package/examples/game/libs/Echo/init.luau +417 -0
  79. package/examples/game/libs/Ember/init.luau +365 -0
  80. package/examples/game/libs/Flare/init.luau +717 -0
  81. package/examples/game/libs/Gleam/init.luau +729 -0
  82. package/examples/game/libs/Guide/init.luau +215 -0
  83. package/examples/game/libs/Helm/init.luau +377 -0
  84. package/examples/game/libs/Hive/init.luau +241 -0
  85. package/examples/game/libs/Keep/Client.luau +114 -0
  86. package/examples/game/libs/Keep/Data.luau +883 -0
  87. package/examples/game/libs/Keep/Engine.luau +211 -0
  88. package/examples/game/libs/Keep/Enum.luau +6 -0
  89. package/examples/game/libs/Keep/Enums.luau +10 -0
  90. package/examples/game/libs/Keep/Path.luau +236 -0
  91. package/examples/game/libs/Keep/Replicator.luau +298 -0
  92. package/examples/game/libs/Keep/Server.luau +251 -0
  93. package/examples/game/libs/Keep/Store.luau +652 -0
  94. package/examples/game/libs/Keep/Trade.luau +455 -0
  95. package/examples/game/libs/Keep/init.luau +205 -0
  96. package/examples/game/libs/Lens/init.luau +609 -0
  97. package/examples/game/libs/Mint/init.luau +279 -0
  98. package/examples/game/libs/Net/init.luau +156 -0
  99. package/examples/game/libs/Net/src/codec.luau +224 -0
  100. package/examples/game/libs/Occlude/init.luau +117 -0
  101. package/examples/game/libs/Pin/init.luau +309 -0
  102. package/examples/game/libs/Promise/LICENSE +21 -0
  103. package/examples/game/libs/Promise/_impl/LICENSE +21 -0
  104. package/examples/game/libs/Promise/_impl/init.lua +1991 -0
  105. package/examples/game/libs/Promise/init.lua +1990 -0
  106. package/examples/game/libs/Promise/init.luau +13 -0
  107. package/examples/game/libs/Promise/types.luau +46 -0
  108. package/examples/game/libs/Roster/init.luau +686 -0
  109. package/examples/game/libs/SOURCES.md +31 -0
  110. package/examples/game/libs/Shift/init.luau +238 -0
  111. package/examples/game/libs/Spark/init.luau +487 -0
  112. package/examples/game/libs/Stage/init.luau +178 -0
  113. package/examples/game/libs/Sweep/init.luau +259 -0
  114. package/examples/game/libs/Trace/init.luau +213 -0
  115. package/examples/game/libs/Ward/init.luau +211 -0
  116. package/examples/game/out/ReplicatedStorage/Shared/Constants/Datas/TemplateData.luau +29 -0
  117. package/examples/game/out/ReplicatedStorage/Shared/Constants/Datas/TemplateData.luau.map.json +13 -0
  118. package/examples/game/out/ReplicatedStorage/Shared/Net/Net.luau +83 -0
  119. package/examples/game/out/ReplicatedStorage/Shared/Net/Net.luau.map.json +13 -0
  120. package/examples/game/out/ServerScriptService/Boot/DataBoot.server.luau +18 -0
  121. package/examples/game/out/ServerScriptService/Boot/DataBoot.server.luau.map.json +13 -0
  122. package/examples/game/out/ServerScriptService/Configuration/ServerConfig.luau +14 -0
  123. package/examples/game/out/ServerScriptService/Configuration/ServerConfig.luau.map.json +13 -0
  124. package/examples/game/out/ServerScriptService/Handlers/Guard.server.luau +15 -0
  125. package/examples/game/out/ServerScriptService/Handlers/Guard.server.luau.map.json +13 -0
  126. package/examples/game/out/ServerScriptService/Handlers/PlayerHandler.server.luau +50 -0
  127. package/examples/game/out/ServerScriptService/Handlers/PlayerHandler.server.luau.map.json +13 -0
  128. package/examples/game/out/StarterPlayer/StarterCharacterScripts/Character/CharacterReady.client.luau +12 -0
  129. package/examples/game/out/StarterPlayer/StarterCharacterScripts/Character/CharacterReady.client.luau.map.json +13 -0
  130. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Controllers/DataController.client.luau +16 -0
  131. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Controllers/DataController.client.luau.map.json +13 -0
  132. package/examples/game/src/ReplicatedStorage/Shared/Net/Net.clh +26 -0
  133. package/examples/game/src/ReplicatedStorage/Shared/Net/Net.flare +5 -0
  134. package/examples/game/src/ServerScriptService/Boot/DataBoot.server.clpp +2 -2
  135. package/examples/game/src/ServerScriptService/Handlers/PlayerHandler.server.clpp +1 -1
  136. package/examples/game/stylua.toml +9 -0
  137. package/generated/api/build-profile.js +115 -0
  138. package/generated/api/cache.js +44 -0
  139. package/generated/api/coverage.js +32 -0
  140. package/generated/api/diff.js +125 -0
  141. package/generated/api/generators/clpp-headers.js +353 -0
  142. package/generated/api/generators/docs-reference.js +7 -0
  143. package/generated/api/generators/lsp-index.js +81 -0
  144. package/generated/api/generators/luau-defs.js +94 -0
  145. package/generated/api/ide.js +76 -0
  146. package/generated/api/identifier.js +47 -0
  147. package/generated/api/index.js +324 -0
  148. package/generated/api/loader.js +57 -0
  149. package/generated/api/merge.js +88 -0
  150. package/generated/api/model.js +5 -0
  151. package/generated/api/normalize.js +231 -0
  152. package/generated/api/overrides-validate.js +58 -0
  153. package/generated/api/perf.js +12 -0
  154. package/generated/api/registry.js +136 -0
  155. package/generated/api/release.js +44 -0
  156. package/generated/api/resolver.js +250 -0
  157. package/generated/api/sources/aux-types-diff.js +75 -0
  158. package/generated/api/sources/client-tracker.js +11 -0
  159. package/generated/api/sources/creator-docs.js +42 -0
  160. package/generated/api/sources/luau-types.js +34 -0
  161. package/generated/api/sources/mini-dump.js +17 -0
  162. package/generated/api/sources/studio-api.js +8 -0
  163. package/generated/api/sources/studio-apiv2.js +11 -0
  164. package/generated/api/sources/studio-full-api.js +8 -0
  165. package/generated/api/studio-smoke.js +80 -0
  166. package/generated/api/type-parse.js +49 -0
  167. package/generated/api/understander.js +51 -0
  168. package/generated/api/validate.js +135 -0
  169. package/generated/api-cli.js +421 -0
  170. package/generated/cli.js +17 -6
  171. package/generated/clpp/contract.js +3 -3
  172. package/generated/clpp/format-luau.js +236 -0
  173. package/generated/clpp/index.js +13 -1
  174. package/generated/clpp/modules.js +227 -0
  175. package/generated/clpp/postprocess.js +86 -5
  176. package/generated/clpp/rewrite-janitor.js +145 -0
  177. package/generated/clpp/runner.js +14 -2
  178. package/generated/compile.js +82 -0
  179. package/generated/flare/index.js +15 -3
  180. package/generated/flare/language.js +561 -0
  181. package/generated/flare/parse.js +19 -7
  182. package/generated/intellisense.js +76 -2
  183. package/generated/lsp.js +118 -7
  184. package/generated/native/index.js +217 -16
  185. package/generated/target/authority-check.js +136 -0
  186. package/generated/target/authority-graph.js +74 -0
  187. package/generated/target/build-cache.js +112 -0
  188. package/generated/target/capabilities.js +89 -0
  189. package/generated/target/capability-profile.js +30 -0
  190. package/generated/target/component-contracts.js +60 -0
  191. package/generated/target/context-check.js +160 -0
  192. package/generated/target/datamodel-check.js +170 -0
  193. package/generated/target/datamodel-generate.js +74 -0
  194. package/generated/target/datamodel.js +233 -0
  195. package/generated/target/diagnostics.js +63 -0
  196. package/generated/target/doctor.js +152 -0
  197. package/generated/target/flare-version.js +58 -0
  198. package/generated/target/get-service.js +69 -0
  199. package/generated/target/lifetime-check.js +47 -0
  200. package/generated/target/luau-stamp.js +39 -0
  201. package/generated/target/optimizer-advise.js +88 -0
  202. package/generated/target/optimizer-apply.js +44 -0
  203. package/generated/target/optimizer-soa.js +254 -0
  204. package/generated/target/parallel-check.js +136 -0
  205. package/generated/target/pgo.js +148 -0
  206. package/generated/target/project-docs.js +69 -0
  207. package/generated/target/project-graph.js +71 -0
  208. package/generated/target/roblox.js +63 -0
  209. package/generated/target/security-check.js +41 -0
  210. package/generated/target/source-map.js +25 -0
  211. package/generated/target/studio-bridge.js +112 -0
  212. package/generated/utils/project.js +178 -8
  213. package/generated/utils/rojo-mapper.js +11 -3
  214. package/include/clpp/datatypes.clh +337 -373
  215. package/include/clpp/generated/enums.clh +1 -1
  216. package/include/clpp/generated/instances.clh +41135 -41064
  217. package/include/clpp/libs/axiom.clh +1 -1
  218. package/include/clpp/libs/hive.clh +1 -0
  219. package/include/clpp/libs/keep.clh +2 -0
  220. package/include/clpp/libs/math.clh +14 -0
  221. package/include/clpp/libs/roster.clh +6 -0
  222. package/include/clpp/libs/spring.clh +17 -0
  223. package/include/clpp/libs/ward.clh +1 -0
  224. package/include/clpp/roblox.clh +3 -4
  225. package/package.json +10 -3
  226. package/plugins/CluauppNav/Plugin.luau +207 -0
  227. package/plugins/CluauppNav/ProfileLogger.luau +27 -0
  228. package/plugins/CluauppNav/README.md +24 -0
  229. package/runtime/Axiom/init.luau +10 -7
  230. package/runtime/Coil/init.luau +8 -2
  231. package/runtime/Crest/init.luau +1 -0
  232. package/runtime/Flare/init.luau +2 -1
  233. package/runtime/Gleam/init.luau +15 -4
  234. package/runtime/Hive/init.luau +36 -14
  235. package/runtime/Pin/init.luau +5 -2
  236. package/runtime/Roster/init.luau +8 -0
  237. package/runtime/Spark/init.luau +3 -0
  238. package/runtime/Sweep/init.luau +2 -0
  239. package/runtime/Trace/init.luau +3 -0
  240. package/runtime/Ward/init.luau +38 -5
  241. package/scripts/postinstall-editor.js +17 -0
  242. package/skills/README.md +13 -0
  243. package/skills/clpp-hive-flare/SKILL.md +79 -0
  244. package/skills/clpp-language/SKILL.md +123 -0
  245. package/skills/clpp-language/compiler.md +89 -0
  246. package/skills/clpp-style/SKILL.md +95 -0
  247. package/src/api/build-profile.ts +138 -0
  248. package/src/api/cache.ts +45 -0
  249. package/src/api/coverage.ts +46 -0
  250. package/src/api/diff.ts +143 -0
  251. package/src/api/generators/clpp-headers.ts +377 -0
  252. package/src/api/generators/docs-reference.ts +11 -0
  253. package/src/api/generators/lsp-index.ts +106 -0
  254. package/src/api/generators/luau-defs.ts +98 -0
  255. package/src/api/ide.ts +87 -0
  256. package/src/api/identifier.ts +47 -0
  257. package/src/api/index.ts +269 -0
  258. package/src/api/loader.ts +60 -0
  259. package/src/api/merge.ts +126 -0
  260. package/src/api/model.ts +283 -0
  261. package/src/api/normalize.ts +257 -0
  262. package/src/api/overrides-validate.ts +58 -0
  263. package/src/api/perf.ts +15 -0
  264. package/src/api/registry.ts +162 -0
  265. package/src/api/release.ts +47 -0
  266. package/src/api/resolver.ts +255 -0
  267. package/src/api/sources/aux-types-diff.ts +81 -0
  268. package/src/api/sources/client-tracker.ts +10 -0
  269. package/src/api/sources/creator-docs.ts +37 -0
  270. package/src/api/sources/luau-types.ts +30 -0
  271. package/src/api/sources/mini-dump.ts +50 -0
  272. package/src/api/sources/studio-api.ts +7 -0
  273. package/src/api/sources/studio-apiv2.ts +10 -0
  274. package/src/api/sources/studio-full-api.ts +7 -0
  275. package/src/api/studio-smoke.ts +82 -0
  276. package/src/api/type-parse.ts +48 -0
  277. package/src/api/understander.ts +72 -0
  278. package/src/api/validate.ts +160 -0
  279. package/src/api-cli.ts +493 -0
  280. package/src/api.generated.js +37 -22
  281. package/src/cli.ts +22 -7
  282. package/src/clpp/contract.ts +34 -3
  283. package/src/clpp/format-luau.ts +262 -0
  284. package/src/clpp/index.ts +13 -2
  285. package/src/clpp/modules.ts +252 -0
  286. package/src/clpp/postprocess.ts +100 -5
  287. package/src/clpp/rewrite-janitor.ts +156 -0
  288. package/src/clpp/runner.ts +14 -2
  289. package/src/compile.ts +87 -0
  290. package/src/flare/index.ts +20 -1
  291. package/src/flare/language.ts +708 -0
  292. package/src/flare/parse.ts +20 -7
  293. package/src/flare/types.ts +2 -0
  294. package/src/intellisense.ts +76 -2
  295. package/src/lsp.ts +151 -8
  296. package/src/native/index.ts +225 -16
  297. package/src/target/authority-check.ts +162 -0
  298. package/src/target/authority-graph.ts +80 -0
  299. package/src/target/build-cache.ts +129 -0
  300. package/src/target/capabilities.ts +115 -0
  301. package/src/target/capability-profile.ts +25 -0
  302. package/src/target/component-contracts.ts +64 -0
  303. package/src/target/context-check.ts +177 -0
  304. package/src/target/datamodel-check.ts +188 -0
  305. package/src/target/datamodel-generate.ts +78 -0
  306. package/src/target/datamodel.ts +261 -0
  307. package/src/target/diagnostics.ts +67 -0
  308. package/src/target/doctor.ts +171 -0
  309. package/src/target/flare-version.ts +61 -0
  310. package/src/target/get-service.ts +85 -0
  311. package/src/target/lifetime-check.ts +45 -0
  312. package/src/target/luau-stamp.ts +33 -0
  313. package/src/target/optimizer-advise.ts +93 -0
  314. package/src/target/optimizer-apply.ts +63 -0
  315. package/src/target/optimizer-soa.ts +270 -0
  316. package/src/target/parallel-check.ts +135 -0
  317. package/src/target/pgo.ts +157 -0
  318. package/src/target/project-docs.ts +68 -0
  319. package/src/target/project-graph.ts +75 -0
  320. package/src/target/roblox.ts +78 -0
  321. package/src/target/security-check.ts +40 -0
  322. package/src/target/source-map.ts +36 -0
  323. package/src/target/studio-bridge.ts +140 -0
  324. package/src/types.ts +14 -0
  325. package/src/utils/project.ts +183 -9
  326. package/src/utils/rojo-mapper.ts +12 -3
  327. package/stylua.toml +9 -0
  328. package/templates/game/.vscode/extensions.json +3 -1
  329. package/templates/game/.vscode/settings.json +8 -1
  330. package/templates/game/include/clpp/datatypes.clh +337 -373
  331. package/templates/game/include/clpp/generated/enums.clh +1 -1
  332. package/templates/game/include/clpp/generated/instances.clh +41135 -41064
  333. package/templates/game/include/clpp/libs/axiom.clh +1 -1
  334. package/templates/game/include/clpp/libs/roster.clh +6 -0
  335. package/templates/game/include/clpp/libs/ward.clh +1 -0
  336. package/templates/game/include/clpp/roblox.clh +3 -4
  337. package/templates/game/src/ReplicatedStorage/Shared/Net/Net.flare +5 -0
  338. package/templates/game/src/ServerScriptService/Boot/DataBoot.server.clpp +2 -2
  339. package/templates/game/src/ServerScriptService/Handlers/PlayerHandler.server.clpp +1 -1
  340. package/templates/game/stylua.toml +9 -0
@@ -0,0 +1,238 @@
1
+ --!native
2
+ --!optimize 2
3
+ --!strict
4
+ -- Shift — compiled FSM/HSM. State ids are u8. No per-frame OnDataChanged.
5
+
6
+ local Shift = {}
7
+ Shift.__index = Shift
8
+
9
+ export type ShiftMachine = typeof(setmetatable(
10
+ {} :: {
11
+ State: number,
12
+ Previous: number,
13
+ History: { number },
14
+ Ids: { [string]: number },
15
+ Names: { [number]: string },
16
+ Parents: { [string]: string },
17
+ Transitions: { { From: string, To: string, Guard: string } },
18
+ Data: { [string]: any },
19
+ Alive: boolean,
20
+ _guards: { [string]: () -> boolean },
21
+ _enters: { [string]: () -> () },
22
+ _leaves: { [string]: () -> () },
23
+ },
24
+ Shift
25
+ ))
26
+
27
+ local function ancestorChain(self: ShiftMachine, name: string?): { string }
28
+ local chain = {}
29
+ local current = name
30
+ local seen: { [string]: boolean } = {}
31
+ while current and not seen[current] do
32
+ seen[current] = true
33
+ table.insert(chain, 1, current)
34
+ current = self.Parents[current]
35
+ end
36
+ return chain
37
+ end
38
+
39
+ local function inBranch(self: ShiftMachine, name: string): boolean
40
+ local current = self.Names[self.State]
41
+ local seen: { [string]: boolean } = {}
42
+ while current and not seen[current] do
43
+ if current == name then
44
+ return true
45
+ end
46
+ seen[current] = true
47
+ current = self.Parents[current]
48
+ end
49
+ return false
50
+ end
51
+
52
+ local function edgeAllowed(self: ShiftMachine, fromName: string?, toName: string): boolean?
53
+ if fromName == nil then
54
+ return nil
55
+ end
56
+ for _, edge in self.Transitions do
57
+ if edge.From == fromName and edge.To == toName then
58
+ if edge.Guard == "" or edge.Guard == nil then
59
+ return true
60
+ end
61
+ local guard = self._guards[edge.Guard]
62
+ if not guard then
63
+ return true
64
+ end
65
+ return guard()
66
+ end
67
+ end
68
+ return nil
69
+ end
70
+
71
+ function Shift.New(ids: { [string]: number }, transitions: { any }, parents: { [string]: string }?): ShiftMachine
72
+ local names = {}
73
+ local initial = 0
74
+ for name, id in ids do
75
+ names[id] = name
76
+ if id == 0 then
77
+ initial = 0
78
+ end
79
+ end
80
+ return setmetatable({
81
+ State = initial,
82
+ Previous = initial,
83
+ History = { initial },
84
+ Ids = ids,
85
+ Names = names,
86
+ Parents = parents or {},
87
+ Transitions = transitions,
88
+ Data = {},
89
+ Alive = true,
90
+ _guards = {} :: { [string]: () -> boolean },
91
+ _enters = {} :: { [string]: () -> () },
92
+ _leaves = {} :: { [string]: () -> () },
93
+ }, Shift)
94
+ end
95
+
96
+ function Shift:Guard(name: string, fn: () -> boolean)
97
+ self._guards[name] = fn
98
+ end
99
+
100
+ function Shift:OnEnter(name: string, fn: () -> ())
101
+ self._enters[name] = fn
102
+ if self.Alive and self.Names[self.State] == name then
103
+ fn()
104
+ end
105
+ end
106
+
107
+ function Shift:OnLeave(name: string, fn: () -> ())
108
+ self._leaves[name] = fn
109
+ end
110
+
111
+ function Shift:CanEnter(toName: string, visiting: { [string]: boolean }?): boolean
112
+ if not self.Alive then
113
+ return false
114
+ end
115
+ if self.Ids[toName] == nil then
116
+ return false
117
+ end
118
+ local fromName = self.Names[self.State]
119
+ if fromName == toName then
120
+ return false
121
+ end
122
+ local explicit = edgeAllowed(self, fromName, toName)
123
+ if explicit ~= nil then
124
+ return explicit
125
+ end
126
+ local parent = self.Parents[toName]
127
+ if parent == nil then
128
+ return false
129
+ end
130
+ if inBranch(self, parent) then
131
+ return true
132
+ end
133
+ local seen = visiting or {}
134
+ if seen[toName] then
135
+ return false
136
+ end
137
+ seen[toName] = true
138
+ return self:CanEnter(parent, seen)
139
+ end
140
+
141
+ local function switchTo(self: ShiftMachine, name: string, id: number)
142
+ local fromName = self.Names[self.State]
143
+ local leaveChain = ancestorChain(self, fromName)
144
+ local enterChain = ancestorChain(self, name)
145
+ local common = 0
146
+ local limit = math.min(#leaveChain, #enterChain)
147
+ for index = 1, limit do
148
+ if leaveChain[index] == enterChain[index] then
149
+ common = index
150
+ else
151
+ break
152
+ end
153
+ end
154
+ for index = #leaveChain, common + 1, -1 do
155
+ local fn = self._leaves[leaveChain[index]]
156
+ if fn then
157
+ fn()
158
+ end
159
+ end
160
+ self.Previous = self.State
161
+ self.State = id
162
+ table.insert(self.History, id)
163
+ for index = common + 1, #enterChain do
164
+ local fn = self._enters[enterChain[index]]
165
+ if fn then
166
+ fn()
167
+ end
168
+ end
169
+ end
170
+
171
+ function Shift:ChangeState(name: string)
172
+ if not self.Alive then
173
+ return
174
+ end
175
+ local id = self.Ids[name]
176
+ if id == nil then
177
+ return
178
+ end
179
+ if self.State == id then
180
+ return
181
+ end
182
+ if not self:CanEnter(name) then
183
+ return
184
+ end
185
+ switchTo(self, name, id)
186
+ end
187
+
188
+ function Shift:GetState(): string
189
+ return self.Names[self.State] or ""
190
+ end
191
+
192
+ function Shift:GetCurrentState(): string
193
+ return self:GetState()
194
+ end
195
+
196
+ function Shift:GetPreviousState(): string
197
+ return self.Names[self.Previous] or ""
198
+ end
199
+
200
+ function Shift:GetId(): number
201
+ return self.State
202
+ end
203
+
204
+ function Shift:WriteId(): buffer
205
+ local buf = buffer.create(1)
206
+ buffer.writeu8(buf, 0, self.State)
207
+ return buf
208
+ end
209
+
210
+ function Shift:ReadId(buf: buffer)
211
+ if not self.Alive then
212
+ return
213
+ end
214
+ local id = buffer.readu8(buf, 0)
215
+ local name = self.Names[id]
216
+ if name == nil or self.State == id then
217
+ return
218
+ end
219
+ switchTo(self, name, id)
220
+ end
221
+
222
+ function Shift:GetData()
223
+ return self.Data
224
+ end
225
+
226
+ function Shift:ChangeData(index: string, value: any)
227
+ self.Data[index] = value
228
+ end
229
+
230
+ function Shift:Destroy()
231
+ self.Alive = false
232
+ table.clear(self.Data)
233
+ table.clear(self._guards)
234
+ table.clear(self._enters)
235
+ table.clear(self._leaves)
236
+ end
237
+
238
+ return Shift
@@ -0,0 +1,487 @@
1
+ --!native
2
+ --!optimize 2
3
+ --!strict
4
+ -- Spark — engine-grade yield-safe signal.
5
+ -- Intrusive doubly-linked list (O(1) Disconnect, Fire-safe unhook).
6
+ -- Pooled runner threads (GoodSignal). Reentrant Fire is queued, not stacked.
7
+ -- Destroy seals the signal. ConnectParallel never reuses the serial runner.
8
+
9
+ export type SparkConnection = {
10
+ Connected: boolean,
11
+ Disconnect: (self: SparkConnection) -> (),
12
+ Destroy: (self: SparkConnection) -> (),
13
+ }
14
+
15
+ export type Connection = SparkConnection
16
+
17
+ export type Spark<T...> = {
18
+ Fire: (self: Spark<T...>, T...) -> (),
19
+ FireDeferred: (self: Spark<T...>, T...) -> (),
20
+ Connect: (self: Spark<T...>, fn: (T...) -> ()) -> SparkConnection,
21
+ ConnectParallel: (self: Spark<T...>, fn: (T...) -> ()) -> SparkConnection,
22
+ Once: (self: Spark<T...>, fn: (T...) -> ()) -> SparkConnection,
23
+ OnceParallel: (self: Spark<T...>, fn: (T...) -> ()) -> SparkConnection,
24
+ ConnectOnce: (self: Spark<T...>, fn: (T...) -> ()) -> SparkConnection,
25
+ DisconnectAll: (self: Spark<T...>) -> (),
26
+ GetConnections: (self: Spark<T...>) -> { SparkConnection },
27
+ Destroy: (self: Spark<T...>) -> (),
28
+ IsDestroyed: (self: Spark<T...>) -> boolean,
29
+ Wait: (self: Spark<T...>) -> T...,
30
+ }
31
+
32
+ type Handler = (...any) -> ()
33
+ type Packed = { n: number, [number]: any }
34
+
35
+ type ConnectionNode = {
36
+ Connected: boolean,
37
+ _signal: SparkImpl?,
38
+ _fn: Handler?,
39
+ _next: ConnectionNode?,
40
+ _prev: ConnectionNode?,
41
+ _once: boolean,
42
+ _parallel: boolean,
43
+ Disconnect: (self: ConnectionNode) -> (),
44
+ Destroy: (self: ConnectionNode) -> (),
45
+ }
46
+
47
+ type SparkImpl = {
48
+ _head: ConnectionNode?,
49
+ _tail: ConnectionNode?,
50
+ _count: number,
51
+ _destroyed: boolean,
52
+ _firing: boolean,
53
+ _queue: { Packed },
54
+ _queueN: number,
55
+ _proxyHandler: RBXScriptConnection?,
56
+ _yieldedThreads: { [thread]: boolean }?,
57
+ Fire: (self: SparkImpl, ...any) -> (),
58
+ FireDeferred: (self: SparkImpl, ...any) -> (),
59
+ Connect: (self: SparkImpl, fn: Handler) -> ConnectionNode,
60
+ ConnectParallel: (self: SparkImpl, fn: Handler) -> ConnectionNode,
61
+ Once: (self: SparkImpl, fn: Handler) -> ConnectionNode,
62
+ OnceParallel: (self: SparkImpl, fn: Handler) -> ConnectionNode,
63
+ ConnectOnce: (self: SparkImpl, fn: Handler) -> ConnectionNode,
64
+ DisconnectAll: (self: SparkImpl) -> (),
65
+ GetConnections: (self: SparkImpl) -> { SparkConnection },
66
+ Destroy: (self: SparkImpl) -> (),
67
+ IsDestroyed: (self: SparkImpl) -> boolean,
68
+ Wait: (self: SparkImpl) -> ...any,
69
+ }
70
+
71
+ local MAX_CONNECTIONS = 4096
72
+ local MAX_REENTRY = 64
73
+ local PACK_POOL_CAP = 16
74
+ local SNAP_POOL_CAP = 8
75
+
76
+ local freeRunnerThread: thread? = nil
77
+ local packPool: { Packed } = {}
78
+ local packPoolN = 0
79
+ local snapPool: { { ConnectionNode } } = {}
80
+ local snapPoolN = 0
81
+
82
+ local function acquirePack(...: any): Packed
83
+ local packed: Packed
84
+ if packPoolN > 0 then
85
+ packed = packPool[packPoolN]
86
+ packPool[packPoolN] = nil
87
+ packPoolN -= 1
88
+ else
89
+ packed = table.pack(...)
90
+ return packed
91
+ end
92
+ local n = select("#", ...)
93
+ packed.n = n
94
+ for i = 1, n do
95
+ packed[i] = select(i, ...)
96
+ end
97
+ return packed
98
+ end
99
+
100
+ local function releasePack(packed: Packed)
101
+ for i = 1, packed.n do
102
+ packed[i] = nil
103
+ end
104
+ packed.n = 0
105
+ if packPoolN < PACK_POOL_CAP then
106
+ packPoolN += 1
107
+ packPool[packPoolN] = packed
108
+ end
109
+ end
110
+
111
+ local function acquireSnap(): { ConnectionNode }
112
+ if snapPoolN > 0 then
113
+ local snap = snapPool[snapPoolN]
114
+ snapPool[snapPoolN] = nil
115
+ snapPoolN -= 1
116
+ return snap
117
+ end
118
+ return {}
119
+ end
120
+
121
+ local function releaseSnap(snap: { ConnectionNode }, n: number)
122
+ for i = 1, n do
123
+ snap[i] = nil
124
+ end
125
+ if snapPoolN < SNAP_POOL_CAP then
126
+ snapPoolN += 1
127
+ snapPool[snapPoolN] = snap
128
+ end
129
+ end
130
+
131
+ local function acquireRunnerThreadAndCallEventHandler(fn: Handler, ...: any)
132
+ local acquired = freeRunnerThread
133
+ freeRunnerThread = nil
134
+ fn(...)
135
+ freeRunnerThread = acquired
136
+ end
137
+
138
+ local function runEventHandlerInFreeThread(...: any)
139
+ acquireRunnerThreadAndCallEventHandler(...)
140
+ while true do
141
+ acquireRunnerThreadAndCallEventHandler(coroutine.yield())
142
+ end
143
+ end
144
+
145
+ local function runParallelHandler(fn: Handler, ...: any)
146
+ local desynced = pcall(task.desynchronize)
147
+ fn(...)
148
+ if desynced then
149
+ pcall(task.synchronize)
150
+ end
151
+ end
152
+
153
+ local function expectFunction(method: string, fn: Handler)
154
+ if type(fn) ~= "function" then
155
+ error(`Spark.{method} expected function, got {typeof(fn)}`, 4)
156
+ end
157
+ end
158
+
159
+ local Connection = {}
160
+ Connection.__index = Connection
161
+
162
+ local function unlink(node: ConnectionNode)
163
+ local signal = node._signal
164
+ if not signal then
165
+ return
166
+ end
167
+ local prev = node._prev
168
+ local next = node._next
169
+ if prev then
170
+ prev._next = next
171
+ elseif signal._head == node then
172
+ signal._head = next
173
+ end
174
+ if next then
175
+ next._prev = prev
176
+ elseif signal._tail == node then
177
+ signal._tail = prev
178
+ end
179
+ node._prev = nil
180
+ -- Keep _next so an in-flight Fire iterator can continue.
181
+ if node.Connected then
182
+ signal._count -= 1
183
+ if signal._count < 0 then
184
+ signal._count = 0
185
+ end
186
+ end
187
+ end
188
+
189
+ local function clearNode(node: ConnectionNode)
190
+ node._fn = nil
191
+ node._signal = nil
192
+ node._once = false
193
+ node._parallel = false
194
+ end
195
+
196
+ function Connection.Disconnect(self: ConnectionNode)
197
+ if not self.Connected then
198
+ return
199
+ end
200
+ unlink(self)
201
+ self.Connected = false
202
+ clearNode(self)
203
+ end
204
+
205
+ Connection.Destroy = Connection.Disconnect
206
+
207
+ local function deadConnection(): ConnectionNode
208
+ return setmetatable({
209
+ Connected = false,
210
+ _signal = nil,
211
+ _fn = nil,
212
+ _next = nil,
213
+ _prev = nil,
214
+ _once = false,
215
+ _parallel = false,
216
+ }, Connection) :: any
217
+ end
218
+
219
+ local Spark = {}
220
+ Spark.__index = Spark
221
+
222
+ local function invoke(conn: ConnectionNode, ...: any)
223
+ local fn = conn._fn
224
+ if not fn then
225
+ return
226
+ end
227
+ local parallel = conn._parallel
228
+ if conn._once then
229
+ conn:Disconnect()
230
+ end
231
+ if parallel then
232
+ task.spawn(runParallelHandler, fn, ...)
233
+ return
234
+ end
235
+ if not freeRunnerThread then
236
+ freeRunnerThread = coroutine.create(runEventHandlerInFreeThread)
237
+ end
238
+ task.spawn(freeRunnerThread :: thread, fn, ...)
239
+ end
240
+
241
+ local function dispatchList(self: SparkImpl, ...: any)
242
+ local item = self._head
243
+ while item do
244
+ if item.Connected then
245
+ invoke(item, ...)
246
+ end
247
+ item = item._next
248
+ end
249
+ end
250
+
251
+ local function bind(self: SparkImpl, fn: Handler, once: boolean, parallel: boolean): ConnectionNode
252
+ if self._destroyed then
253
+ return deadConnection()
254
+ end
255
+ expectFunction(if parallel then "ConnectParallel" else "Connect", fn)
256
+ if self._count >= MAX_CONNECTIONS then
257
+ warn(`Spark: connection cap ({MAX_CONNECTIONS}) reached; refusing Connect`)
258
+ return deadConnection()
259
+ end
260
+
261
+ local node = setmetatable({
262
+ Connected = true,
263
+ _signal = self,
264
+ _fn = fn,
265
+ _next = self._head,
266
+ _prev = nil,
267
+ _once = once,
268
+ _parallel = parallel,
269
+ }, Connection) :: ConnectionNode
270
+
271
+ if self._head then
272
+ self._head._prev = node
273
+ else
274
+ self._tail = node
275
+ end
276
+ self._head = node
277
+ self._count += 1
278
+ return node
279
+ end
280
+
281
+ function Spark.new<T...>(): Spark<T...>
282
+ return setmetatable({
283
+ _head = nil,
284
+ _tail = nil,
285
+ _count = 0,
286
+ _destroyed = false,
287
+ _firing = false,
288
+ _queue = {},
289
+ _queueN = 0,
290
+ _proxyHandler = nil,
291
+ _yieldedThreads = nil,
292
+ }, Spark) :: any
293
+ end
294
+
295
+ Spark.New = Spark.new
296
+
297
+ function Spark.Wrap<T...>(rbxScriptSignal: RBXScriptSignal): Spark<T...>
298
+ if typeof(rbxScriptSignal) ~= "RBXScriptSignal" then
299
+ error("Spark.Wrap expected RBXScriptSignal, got " .. typeof(rbxScriptSignal), 2)
300
+ end
301
+ local spark = Spark.new() :: SparkImpl
302
+ spark._proxyHandler = rbxScriptSignal:Connect(function(...: any)
303
+ if not spark._destroyed then
304
+ spark:Fire(...)
305
+ end
306
+ end)
307
+ return spark :: any
308
+ end
309
+
310
+ function Spark.Is(obj: any): boolean
311
+ return type(obj) == "table" and getmetatable(obj) == Spark
312
+ end
313
+
314
+ function Spark.Connect(self: SparkImpl, fn: Handler): ConnectionNode
315
+ return bind(self, fn, false, false)
316
+ end
317
+
318
+ function Spark.ConnectParallel(self: SparkImpl, fn: Handler): ConnectionNode
319
+ return bind(self, fn, false, true)
320
+ end
321
+
322
+ function Spark.Once(self: SparkImpl, fn: Handler): ConnectionNode
323
+ return bind(self, fn, true, false)
324
+ end
325
+
326
+ function Spark.OnceParallel(self: SparkImpl, fn: Handler): ConnectionNode
327
+ return bind(self, fn, true, true)
328
+ end
329
+
330
+ function Spark.ConnectOnce(self: SparkImpl, fn: Handler): ConnectionNode
331
+ return bind(self, fn, true, false)
332
+ end
333
+
334
+ function Spark.GetConnections(self: SparkImpl): { SparkConnection }
335
+ local items = table.create(self._count)
336
+ local i = 0
337
+ local item = self._head
338
+ while item do
339
+ if item.Connected then
340
+ i += 1
341
+ items[i] = item
342
+ end
343
+ item = item._next
344
+ end
345
+ return items
346
+ end
347
+
348
+ function Spark.IsDestroyed(self: SparkImpl): boolean
349
+ return self._destroyed
350
+ end
351
+
352
+ function Spark.DisconnectAll(self: SparkImpl)
353
+ if not self then
354
+ return
355
+ end
356
+ local item = self._head
357
+ self._head = nil
358
+ self._tail = nil
359
+ self._count = 0
360
+ while item do
361
+ local next = item._next
362
+ item.Connected = false
363
+ clearNode(item)
364
+ item = next
365
+ end
366
+
367
+ local yielded = self._yieldedThreads
368
+ if not yielded then
369
+ return
370
+ end
371
+ for thread in yielded do
372
+ if coroutine.status(thread) == "suspended" then
373
+ warn(debug.traceback(thread, "spark disconnected; yielded thread cancelled", 2))
374
+ task.cancel(thread)
375
+ end
376
+ end
377
+ table.clear(yielded)
378
+ end
379
+
380
+ function Spark.Fire(self: SparkImpl, ...: any)
381
+ if self._destroyed or self._count == 0 then
382
+ return
383
+ end
384
+ if self._firing then
385
+ if self._queueN >= MAX_REENTRY then
386
+ warn("Spark: reentrant Fire dropped (queue full)")
387
+ return
388
+ end
389
+ self._queueN += 1
390
+ self._queue[self._queueN] = acquirePack(...)
391
+ return
392
+ end
393
+
394
+ self._firing = true
395
+ dispatchList(self, ...)
396
+ local i = 1
397
+ while i <= self._queueN do
398
+ local packed = self._queue[i]
399
+ self._queue[i] = nil
400
+ dispatchList(self, table.unpack(packed, 1, packed.n))
401
+ releasePack(packed)
402
+ i += 1
403
+ end
404
+ self._queueN = 0
405
+ self._firing = false
406
+ end
407
+
408
+ local function deferredFlush(snap: { ConnectionNode }, n: number, packed: Packed)
409
+ for i = 1, n do
410
+ local conn = snap[i]
411
+ if conn and conn.Connected then
412
+ invoke(conn, table.unpack(packed, 1, packed.n))
413
+ end
414
+ end
415
+ releaseSnap(snap, n)
416
+ releasePack(packed)
417
+ end
418
+
419
+ function Spark.FireDeferred(self: SparkImpl, ...: any)
420
+ if self._destroyed or self._count == 0 then
421
+ return
422
+ end
423
+ local snap = acquireSnap()
424
+ local n = 0
425
+ local item = self._head
426
+ while item do
427
+ if item.Connected then
428
+ n += 1
429
+ snap[n] = item
430
+ end
431
+ item = item._next
432
+ end
433
+ if n == 0 then
434
+ releaseSnap(snap, 0)
435
+ return
436
+ end
437
+ task.defer(deferredFlush, snap, n, acquirePack(...))
438
+ end
439
+
440
+ function Spark.Wait(self: SparkImpl): ...any
441
+ if self._destroyed then
442
+ error("Spark.Wait on destroyed signal", 2)
443
+ end
444
+
445
+ local yielded = self._yieldedThreads
446
+ if not yielded then
447
+ yielded = {}
448
+ self._yieldedThreads = yielded
449
+ end
450
+
451
+ local thread = coroutine.running()
452
+ yielded[thread] = true
453
+ local pending: Packed? = nil
454
+
455
+ self:Once(function(...: any)
456
+ yielded[thread] = nil
457
+ if coroutine.status(thread) == "suspended" then
458
+ task.spawn(thread, ...)
459
+ else
460
+ pending = table.pack(...)
461
+ end
462
+ end)
463
+
464
+ if pending then
465
+ return table.unpack(pending, 1, pending.n)
466
+ end
467
+ return coroutine.yield()
468
+ end
469
+
470
+ function Spark.Destroy(self: SparkImpl)
471
+ if self._destroyed then
472
+ return
473
+ end
474
+ self._destroyed = true
475
+ self:DisconnectAll()
476
+ local proxy = self._proxyHandler
477
+ if proxy then
478
+ proxy:Disconnect()
479
+ self._proxyHandler = nil
480
+ end
481
+ table.clear(self._queue)
482
+ self._queueN = 0
483
+ end
484
+
485
+ Spark.Connection = Connection
486
+
487
+ return Spark