@quenty/studio-bridge 0.21.1 → 0.22.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 (482) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +109 -17
  3. package/build-scripts/transform-add-session-attribute.lua +1 -0
  4. package/deploy.nevermore.json +10 -0
  5. package/dist/src/bridge/bridge-connection.d.ts +26 -2
  6. package/dist/src/bridge/bridge-connection.d.ts.map +1 -1
  7. package/dist/src/bridge/bridge-connection.js +159 -14
  8. package/dist/src/bridge/bridge-connection.js.map +1 -1
  9. package/dist/src/bridge/bridge-connection.test.js +81 -0
  10. package/dist/src/bridge/bridge-connection.test.js.map +1 -1
  11. package/dist/src/bridge/bridge-session.d.ts +35 -2
  12. package/dist/src/bridge/bridge-session.d.ts.map +1 -1
  13. package/dist/src/bridge/bridge-session.js +185 -28
  14. package/dist/src/bridge/bridge-session.js.map +1 -1
  15. package/dist/src/bridge/bridge-session.test.js +6 -4
  16. package/dist/src/bridge/bridge-session.test.js.map +1 -1
  17. package/dist/src/bridge/index.d.ts +1 -1
  18. package/dist/src/bridge/index.d.ts.map +1 -1
  19. package/dist/src/bridge/index.js.map +1 -1
  20. package/dist/src/bridge/internal/bridge-client.d.ts +4 -0
  21. package/dist/src/bridge/internal/bridge-client.d.ts.map +1 -1
  22. package/dist/src/bridge/internal/bridge-client.js +20 -7
  23. package/dist/src/bridge/internal/bridge-client.js.map +1 -1
  24. package/dist/src/bridge/internal/bridge-client.test.js +18 -0
  25. package/dist/src/bridge/internal/bridge-client.test.js.map +1 -1
  26. package/dist/src/bridge/internal/bridge-host.d.ts +13 -1
  27. package/dist/src/bridge/internal/bridge-host.d.ts.map +1 -1
  28. package/dist/src/bridge/internal/bridge-host.js +35 -0
  29. package/dist/src/bridge/internal/bridge-host.js.map +1 -1
  30. package/dist/src/bridge/internal/bridge-host.test.js +31 -0
  31. package/dist/src/bridge/internal/bridge-host.test.js.map +1 -1
  32. package/dist/src/bridge/internal/hand-off.d.ts.map +1 -1
  33. package/dist/src/bridge/internal/hand-off.js +4 -1
  34. package/dist/src/bridge/internal/hand-off.js.map +1 -1
  35. package/dist/src/bridge/internal/host-protocol.d.ts +8 -0
  36. package/dist/src/bridge/internal/host-protocol.d.ts.map +1 -1
  37. package/dist/src/bridge/internal/host-protocol.js +120 -13
  38. package/dist/src/bridge/internal/host-protocol.js.map +1 -1
  39. package/dist/src/bridge/internal/host-protocol.test.js +13 -0
  40. package/dist/src/bridge/internal/host-protocol.test.js.map +1 -1
  41. package/dist/src/bridge/internal/instance-state.d.ts +9 -0
  42. package/dist/src/bridge/internal/instance-state.d.ts.map +1 -0
  43. package/dist/src/bridge/internal/instance-state.js +28 -0
  44. package/dist/src/bridge/internal/instance-state.js.map +1 -0
  45. package/dist/src/bridge/internal/transport-client.d.ts.map +1 -1
  46. package/dist/src/bridge/internal/transport-client.js +2 -1
  47. package/dist/src/bridge/internal/transport-client.js.map +1 -1
  48. package/dist/src/bridge/internal/transport-server.d.ts.map +1 -1
  49. package/dist/src/bridge/internal/transport-server.js +5 -1
  50. package/dist/src/bridge/internal/transport-server.js.map +1 -1
  51. package/dist/src/bridge/types.d.ts +43 -4
  52. package/dist/src/bridge/types.d.ts.map +1 -1
  53. package/dist/src/bridge/types.js.map +1 -1
  54. package/dist/src/cli/adapters/cli-command-adapter-handler.test.js +24 -0
  55. package/dist/src/cli/adapters/cli-command-adapter-handler.test.js.map +1 -1
  56. package/dist/src/cli/adapters/cli-command-adapter.d.ts +6 -0
  57. package/dist/src/cli/adapters/cli-command-adapter.d.ts.map +1 -1
  58. package/dist/src/cli/adapters/cli-command-adapter.js +107 -11
  59. package/dist/src/cli/adapters/cli-command-adapter.js.map +1 -1
  60. package/dist/src/cli/adapters/group-builder.d.ts +3 -0
  61. package/dist/src/cli/adapters/group-builder.d.ts.map +1 -1
  62. package/dist/src/cli/adapters/group-builder.js +34 -15
  63. package/dist/src/cli/adapters/group-builder.js.map +1 -1
  64. package/dist/src/cli/adapters/group-builder.test.js +26 -2
  65. package/dist/src/cli/adapters/group-builder.test.js.map +1 -1
  66. package/dist/src/cli/adapters/help-formatter.d.ts.map +1 -1
  67. package/dist/src/cli/adapters/help-formatter.js +4 -3
  68. package/dist/src/cli/adapters/help-formatter.js.map +1 -1
  69. package/dist/src/cli/adapters/help-formatter.test.js +1 -1
  70. package/dist/src/cli/adapters/help-formatter.test.js.map +1 -1
  71. package/dist/src/cli/adapters/playtest-edit-guard.test.d.ts +2 -0
  72. package/dist/src/cli/adapters/playtest-edit-guard.test.d.ts.map +1 -0
  73. package/dist/src/cli/adapters/playtest-edit-guard.test.js +28 -0
  74. package/dist/src/cli/adapters/playtest-edit-guard.test.js.map +1 -0
  75. package/dist/src/cli/args/global-args.d.ts +1 -1
  76. package/dist/src/cli/args/global-args.d.ts.map +1 -1
  77. package/dist/src/cli/cli-output-context.d.ts +14 -0
  78. package/dist/src/cli/cli-output-context.d.ts.map +1 -0
  79. package/dist/src/cli/cli-output-context.js +39 -0
  80. package/dist/src/cli/cli-output-context.js.map +1 -0
  81. package/dist/src/cli/cli-output-context.test.d.ts +2 -0
  82. package/dist/src/cli/cli-output-context.test.d.ts.map +1 -0
  83. package/dist/src/cli/cli-output-context.test.js +38 -0
  84. package/dist/src/cli/cli-output-context.test.js.map +1 -0
  85. package/dist/src/cli/cli.js +40 -17
  86. package/dist/src/cli/cli.js.map +1 -1
  87. package/dist/src/cli/managed-studio-run.d.ts +21 -0
  88. package/dist/src/cli/managed-studio-run.d.ts.map +1 -0
  89. package/dist/src/cli/managed-studio-run.js +75 -0
  90. package/dist/src/cli/managed-studio-run.js.map +1 -0
  91. package/dist/src/cli/managed-studio-run.test.d.ts +2 -0
  92. package/dist/src/cli/managed-studio-run.test.d.ts.map +1 -0
  93. package/dist/src/cli/managed-studio-run.test.js +52 -0
  94. package/dist/src/cli/managed-studio-run.test.js.map +1 -0
  95. package/dist/src/cli/resolve-session.js +1 -1
  96. package/dist/src/cli/resolve-session.js.map +1 -1
  97. package/dist/src/cli/resolve-session.test.js +1 -1
  98. package/dist/src/cli/resolve-session.test.js.map +1 -1
  99. package/dist/src/cli/script-executor.d.ts +13 -1
  100. package/dist/src/cli/script-executor.d.ts.map +1 -1
  101. package/dist/src/cli/script-executor.js +77 -47
  102. package/dist/src/cli/script-executor.js.map +1 -1
  103. package/dist/src/commands/console/exec/exec.d.ts +17 -0
  104. package/dist/src/commands/console/exec/exec.d.ts.map +1 -1
  105. package/dist/src/commands/console/exec/exec.js +39 -7
  106. package/dist/src/commands/console/exec/exec.js.map +1 -1
  107. package/dist/src/commands/console/exec/exec.test.js +62 -3
  108. package/dist/src/commands/console/exec/exec.test.js.map +1 -1
  109. package/dist/src/commands/console/logs/logs.d.ts +30 -2
  110. package/dist/src/commands/console/logs/logs.d.ts.map +1 -1
  111. package/dist/src/commands/console/logs/logs.js +82 -7
  112. package/dist/src/commands/console/logs/logs.js.map +1 -1
  113. package/dist/src/commands/console/logs/logs.test.js +63 -4
  114. package/dist/src/commands/console/logs/logs.test.js.map +1 -1
  115. package/dist/src/commands/explorer/export/export.d.ts +19 -0
  116. package/dist/src/commands/explorer/export/export.d.ts.map +1 -0
  117. package/dist/src/commands/explorer/export/export.js +37 -0
  118. package/dist/src/commands/explorer/export/export.js.map +1 -0
  119. package/dist/src/commands/explorer/export/export.test.d.ts +2 -0
  120. package/dist/src/commands/explorer/export/export.test.d.ts.map +1 -0
  121. package/dist/src/commands/explorer/export/export.test.js +25 -0
  122. package/dist/src/commands/explorer/export/export.test.js.map +1 -0
  123. package/dist/src/commands/explorer/find/find.d.ts +28 -0
  124. package/dist/src/commands/explorer/find/find.d.ts.map +1 -0
  125. package/dist/src/commands/explorer/find/find.js +75 -0
  126. package/dist/src/commands/explorer/find/find.js.map +1 -0
  127. package/dist/src/commands/explorer/find/find.test.d.ts +2 -0
  128. package/dist/src/commands/explorer/find/find.test.d.ts.map +1 -0
  129. package/dist/src/commands/explorer/find/find.test.js +44 -0
  130. package/dist/src/commands/explorer/find/find.test.js.map +1 -0
  131. package/dist/src/commands/explorer/grep/grep.d.ts +24 -0
  132. package/dist/src/commands/explorer/grep/grep.d.ts.map +1 -0
  133. package/dist/src/commands/explorer/grep/grep.js +63 -0
  134. package/dist/src/commands/explorer/grep/grep.js.map +1 -0
  135. package/dist/src/commands/explorer/grep/grep.test.d.ts +2 -0
  136. package/dist/src/commands/explorer/grep/grep.test.d.ts.map +1 -0
  137. package/dist/src/commands/explorer/grep/grep.test.js +42 -0
  138. package/dist/src/commands/explorer/grep/grep.test.js.map +1 -0
  139. package/dist/src/commands/explorer/import/import.d.ts +21 -0
  140. package/dist/src/commands/explorer/import/import.d.ts.map +1 -0
  141. package/dist/src/commands/explorer/import/import.js +37 -0
  142. package/dist/src/commands/explorer/import/import.js.map +1 -0
  143. package/dist/src/commands/explorer/import/import.test.d.ts +2 -0
  144. package/dist/src/commands/explorer/import/import.test.d.ts.map +1 -0
  145. package/dist/src/commands/explorer/import/import.test.js +45 -0
  146. package/dist/src/commands/explorer/import/import.test.js.map +1 -0
  147. package/dist/src/commands/formatters.test.js +7 -1
  148. package/dist/src/commands/formatters.test.js.map +1 -1
  149. package/dist/src/commands/framework/action-loader.d.ts +5 -1
  150. package/dist/src/commands/framework/action-loader.d.ts.map +1 -1
  151. package/dist/src/commands/framework/action-loader.js +9 -2
  152. package/dist/src/commands/framework/action-loader.js.map +1 -1
  153. package/dist/src/commands/framework/action-loader.test.js +23 -0
  154. package/dist/src/commands/framework/action-loader.test.js.map +1 -1
  155. package/dist/src/commands/framework/arg-builder.d.ts +15 -2
  156. package/dist/src/commands/framework/arg-builder.d.ts.map +1 -1
  157. package/dist/src/commands/framework/arg-builder.js +22 -2
  158. package/dist/src/commands/framework/arg-builder.js.map +1 -1
  159. package/dist/src/commands/framework/define-command.d.ts +19 -3
  160. package/dist/src/commands/framework/define-command.d.ts.map +1 -1
  161. package/dist/src/commands/framework/define-command.js.map +1 -1
  162. package/dist/src/commands/framework/index.d.ts +1 -1
  163. package/dist/src/commands/framework/index.d.ts.map +1 -1
  164. package/dist/src/commands/framework/index.js.map +1 -1
  165. package/dist/src/commands/index.d.ts +11 -0
  166. package/dist/src/commands/index.d.ts.map +1 -1
  167. package/dist/src/commands/index.js +11 -0
  168. package/dist/src/commands/index.js.map +1 -1
  169. package/dist/src/commands/linux/inject-credentials/inject-credentials.d.ts.map +1 -1
  170. package/dist/src/commands/linux/inject-credentials/inject-credentials.js +1 -3
  171. package/dist/src/commands/linux/inject-credentials/inject-credentials.js.map +1 -1
  172. package/dist/src/commands/linux/setup/setup.d.ts.map +1 -1
  173. package/dist/src/commands/linux/setup/setup.js +1 -3
  174. package/dist/src/commands/linux/setup/setup.js.map +1 -1
  175. package/dist/src/commands/linux/status/status.d.ts +9 -1
  176. package/dist/src/commands/linux/status/status.d.ts.map +1 -1
  177. package/dist/src/commands/linux/status/status.js +66 -86
  178. package/dist/src/commands/linux/status/status.js.map +1 -1
  179. package/dist/src/commands/playtest/player/add/add.d.ts +17 -0
  180. package/dist/src/commands/playtest/player/add/add.d.ts.map +1 -0
  181. package/dist/src/commands/playtest/player/add/add.js +45 -0
  182. package/dist/src/commands/playtest/player/add/add.js.map +1 -0
  183. package/dist/src/commands/playtest/player/list/list.d.ts +13 -0
  184. package/dist/src/commands/playtest/player/list/list.d.ts.map +1 -0
  185. package/dist/src/commands/playtest/player/list/list.js +33 -0
  186. package/dist/src/commands/playtest/player/list/list.js.map +1 -0
  187. package/dist/src/commands/playtest/player/playtest-players.d.ts +36 -0
  188. package/dist/src/commands/playtest/player/playtest-players.d.ts.map +1 -0
  189. package/dist/src/commands/playtest/player/playtest-players.js +187 -0
  190. package/dist/src/commands/playtest/player/playtest-players.js.map +1 -0
  191. package/dist/src/commands/playtest/player/playtest-players.test.d.ts +2 -0
  192. package/dist/src/commands/playtest/player/playtest-players.test.d.ts.map +1 -0
  193. package/dist/src/commands/playtest/player/playtest-players.test.js +89 -0
  194. package/dist/src/commands/playtest/player/playtest-players.test.js.map +1 -0
  195. package/dist/src/commands/playtest/player/remove/remove.d.ts +17 -0
  196. package/dist/src/commands/playtest/player/remove/remove.d.ts.map +1 -0
  197. package/dist/src/commands/playtest/player/remove/remove.js +42 -0
  198. package/dist/src/commands/playtest/player/remove/remove.js.map +1 -0
  199. package/dist/src/commands/playtest/playtest-sessions.d.ts +49 -0
  200. package/dist/src/commands/playtest/playtest-sessions.d.ts.map +1 -0
  201. package/dist/src/commands/playtest/playtest-sessions.js +283 -0
  202. package/dist/src/commands/playtest/playtest-sessions.js.map +1 -0
  203. package/dist/src/commands/playtest/playtest-sessions.test.d.ts +2 -0
  204. package/dist/src/commands/playtest/playtest-sessions.test.d.ts.map +1 -0
  205. package/dist/src/commands/playtest/playtest-sessions.test.js +87 -0
  206. package/dist/src/commands/playtest/playtest-sessions.test.js.map +1 -0
  207. package/dist/src/commands/playtest/restart/restart.d.ts +23 -0
  208. package/dist/src/commands/playtest/restart/restart.d.ts.map +1 -0
  209. package/dist/src/commands/playtest/restart/restart.js +105 -0
  210. package/dist/src/commands/playtest/restart/restart.js.map +1 -0
  211. package/dist/src/commands/playtest/run/run.d.ts +47 -0
  212. package/dist/src/commands/playtest/run/run.d.ts.map +1 -0
  213. package/dist/src/commands/playtest/run/run.js +137 -0
  214. package/dist/src/commands/playtest/run/run.js.map +1 -0
  215. package/dist/src/commands/playtest/start/start.d.ts +42 -0
  216. package/dist/src/commands/playtest/start/start.d.ts.map +1 -0
  217. package/dist/src/commands/playtest/start/start.js +70 -0
  218. package/dist/src/commands/playtest/start/start.js.map +1 -0
  219. package/dist/src/commands/playtest/stop/stop.d.ts +15 -0
  220. package/dist/src/commands/playtest/stop/stop.d.ts.map +1 -0
  221. package/dist/src/commands/playtest/stop/stop.js +24 -0
  222. package/dist/src/commands/playtest/stop/stop.js.map +1 -0
  223. package/dist/src/commands/process/close/close.d.ts +19 -10
  224. package/dist/src/commands/process/close/close.d.ts.map +1 -1
  225. package/dist/src/commands/process/close/close.js +118 -15
  226. package/dist/src/commands/process/close/close.js.map +1 -1
  227. package/dist/src/commands/process/close/close.test.d.ts +2 -0
  228. package/dist/src/commands/process/close/close.test.d.ts.map +1 -0
  229. package/dist/src/commands/process/close/close.test.js +190 -0
  230. package/dist/src/commands/process/close/close.test.js.map +1 -0
  231. package/dist/src/commands/process/info/info.d.ts +45 -5
  232. package/dist/src/commands/process/info/info.d.ts.map +1 -1
  233. package/dist/src/commands/process/info/info.js +117 -13
  234. package/dist/src/commands/process/info/info.js.map +1 -1
  235. package/dist/src/commands/process/info/info.test.js +140 -38
  236. package/dist/src/commands/process/info/info.test.js.map +1 -1
  237. package/dist/src/commands/process/launch/launch.d.ts +11 -1
  238. package/dist/src/commands/process/launch/launch.d.ts.map +1 -1
  239. package/dist/src/commands/process/launch/launch.js +37 -7
  240. package/dist/src/commands/process/launch/launch.js.map +1 -1
  241. package/dist/src/commands/process/launch/launch.test.js +99 -20
  242. package/dist/src/commands/process/launch/launch.test.js.map +1 -1
  243. package/dist/src/commands/process/list/list.d.ts +2 -1
  244. package/dist/src/commands/process/list/list.d.ts.map +1 -1
  245. package/dist/src/commands/process/list/list.js +9 -6
  246. package/dist/src/commands/process/list/list.js.map +1 -1
  247. package/dist/src/commands/process/list/list.test.js +1 -0
  248. package/dist/src/commands/process/list/list.test.js.map +1 -1
  249. package/dist/src/commands/process/run/run.d.ts +9 -0
  250. package/dist/src/commands/process/run/run.d.ts.map +1 -1
  251. package/dist/src/commands/process/run/run.js +15 -5
  252. package/dist/src/commands/process/run/run.js.map +1 -1
  253. package/dist/src/commands/serve/serve.d.ts +2 -0
  254. package/dist/src/commands/serve/serve.d.ts.map +1 -1
  255. package/dist/src/commands/serve/serve.js +17 -4
  256. package/dist/src/commands/serve/serve.js.map +1 -1
  257. package/dist/src/commands/serve/serve.test.js +9 -2
  258. package/dist/src/commands/serve/serve.test.js.map +1 -1
  259. package/dist/src/commands/viewport/screenshot/screenshot.d.ts.map +1 -1
  260. package/dist/src/commands/viewport/screenshot/screenshot.js +4 -1
  261. package/dist/src/commands/viewport/screenshot/screenshot.js.map +1 -1
  262. package/dist/src/commands/viewport/screenshot/screenshot.test.js +5 -9
  263. package/dist/src/commands/viewport/screenshot/screenshot.test.js.map +1 -1
  264. package/dist/src/deploy/deploy-places.d.ts +39 -0
  265. package/dist/src/deploy/deploy-places.d.ts.map +1 -0
  266. package/dist/src/deploy/deploy-places.js +162 -0
  267. package/dist/src/deploy/deploy-places.js.map +1 -0
  268. package/dist/src/deploy/deploy-places.test.d.ts +2 -0
  269. package/dist/src/deploy/deploy-places.test.d.ts.map +1 -0
  270. package/dist/src/deploy/deploy-places.test.js +96 -0
  271. package/dist/src/deploy/deploy-places.test.js.map +1 -0
  272. package/dist/src/index.d.ts +2 -2
  273. package/dist/src/index.d.ts.map +1 -1
  274. package/dist/src/index.js +1 -1
  275. package/dist/src/index.js.map +1 -1
  276. package/dist/src/plugin/persistent-plugin-installer.d.ts.map +1 -1
  277. package/dist/src/plugin/persistent-plugin-installer.js +2 -1
  278. package/dist/src/plugin/persistent-plugin-installer.js.map +1 -1
  279. package/dist/src/plugin/persistent-plugin-installer.test.js +3 -0
  280. package/dist/src/plugin/persistent-plugin-installer.test.js.map +1 -1
  281. package/dist/src/plugin/plugin-build-version.d.ts +7 -0
  282. package/dist/src/plugin/plugin-build-version.d.ts.map +1 -0
  283. package/dist/src/plugin/plugin-build-version.js +42 -0
  284. package/dist/src/plugin/plugin-build-version.js.map +1 -0
  285. package/dist/src/plugin/plugin-build-version.test.d.ts +2 -0
  286. package/dist/src/plugin/plugin-build-version.test.d.ts.map +1 -0
  287. package/dist/src/plugin/plugin-build-version.test.js +41 -0
  288. package/dist/src/plugin/plugin-build-version.test.js.map +1 -0
  289. package/dist/src/plugin/plugin-injector.d.ts.map +1 -1
  290. package/dist/src/plugin/plugin-injector.js +7 -1
  291. package/dist/src/plugin/plugin-injector.js.map +1 -1
  292. package/dist/src/process/studio-process-closer.d.ts +35 -0
  293. package/dist/src/process/studio-process-closer.d.ts.map +1 -0
  294. package/dist/src/process/studio-process-closer.js +129 -0
  295. package/dist/src/process/studio-process-closer.js.map +1 -0
  296. package/dist/src/process/studio-process-closer.test.d.ts +2 -0
  297. package/dist/src/process/studio-process-closer.test.d.ts.map +1 -0
  298. package/dist/src/process/studio-process-closer.test.js +80 -0
  299. package/dist/src/process/studio-process-closer.test.js.map +1 -0
  300. package/dist/src/process/studio-settings.d.ts +14 -0
  301. package/dist/src/process/studio-settings.d.ts.map +1 -0
  302. package/dist/src/process/studio-settings.js +50 -0
  303. package/dist/src/process/studio-settings.js.map +1 -0
  304. package/dist/src/process/studio-settings.test.d.ts +2 -0
  305. package/dist/src/process/studio-settings.test.d.ts.map +1 -0
  306. package/dist/src/process/studio-settings.test.js +16 -0
  307. package/dist/src/process/studio-settings.test.js.map +1 -0
  308. package/dist/src/process/studio-window-state.d.ts +13 -0
  309. package/dist/src/process/studio-window-state.d.ts.map +1 -0
  310. package/dist/src/process/studio-window-state.js +48 -0
  311. package/dist/src/process/studio-window-state.js.map +1 -0
  312. package/dist/src/server/studio-bridge-server.d.ts +3 -1
  313. package/dist/src/server/studio-bridge-server.d.ts.map +1 -1
  314. package/dist/src/server/studio-bridge-server.js +12 -4
  315. package/dist/src/server/studio-bridge-server.js.map +1 -1
  316. package/dist/src/server/web-socket-protocol.d.ts +173 -6
  317. package/dist/src/server/web-socket-protocol.d.ts.map +1 -1
  318. package/dist/src/server/web-socket-protocol.js +244 -1
  319. package/dist/src/server/web-socket-protocol.js.map +1 -1
  320. package/dist/src/server/web-socket-protocol.test.js +48 -0
  321. package/dist/src/server/web-socket-protocol.test.js.map +1 -1
  322. package/dist/src/test/e2e/persistent-session.test.js +121 -0
  323. package/dist/src/test/e2e/persistent-session.test.js.map +1 -1
  324. package/dist/src/test/e2e/split-server.test.d.ts +3 -5
  325. package/dist/src/test/e2e/split-server.test.d.ts.map +1 -1
  326. package/dist/src/test/e2e/split-server.test.js +3 -5
  327. package/dist/src/test/e2e/split-server.test.js.map +1 -1
  328. package/dist/tsconfig.tsbuildinfo +1 -1
  329. package/docker/Dockerfile +3 -0
  330. package/package.json +8 -6
  331. package/src/bridge/bridge-connection.test.ts +126 -0
  332. package/src/bridge/bridge-connection.ts +217 -22
  333. package/src/bridge/bridge-session.test.ts +6 -4
  334. package/src/bridge/bridge-session.ts +273 -28
  335. package/src/bridge/index.ts +3 -0
  336. package/src/bridge/internal/bridge-client.test.ts +23 -0
  337. package/src/bridge/internal/bridge-client.ts +25 -7
  338. package/src/bridge/internal/bridge-host.test.ts +38 -0
  339. package/src/bridge/internal/bridge-host.ts +47 -0
  340. package/src/bridge/internal/hand-off.ts +4 -1
  341. package/src/bridge/internal/host-protocol.test.ts +15 -0
  342. package/src/bridge/internal/host-protocol.ts +148 -37
  343. package/src/bridge/internal/instance-state.ts +34 -0
  344. package/src/bridge/internal/transport-client.ts +2 -1
  345. package/src/bridge/internal/transport-server.ts +5 -1
  346. package/src/bridge/types.ts +47 -2
  347. package/src/cli/adapters/cli-command-adapter-handler.test.ts +28 -0
  348. package/src/cli/adapters/cli-command-adapter.ts +150 -18
  349. package/src/cli/adapters/group-builder.test.ts +34 -2
  350. package/src/cli/adapters/group-builder.ts +52 -20
  351. package/src/cli/adapters/help-formatter.test.ts +1 -1
  352. package/src/cli/adapters/help-formatter.ts +4 -3
  353. package/src/cli/adapters/playtest-edit-guard.test.ts +54 -0
  354. package/src/cli/args/global-args.ts +1 -1
  355. package/src/cli/cli-output-context.test.ts +55 -0
  356. package/src/cli/cli-output-context.ts +50 -0
  357. package/src/cli/cli.ts +49 -17
  358. package/src/cli/managed-studio-run.test.ts +71 -0
  359. package/src/cli/managed-studio-run.ts +135 -0
  360. package/src/cli/resolve-session.test.ts +1 -1
  361. package/src/cli/resolve-session.ts +1 -1
  362. package/src/cli/script-executor.ts +99 -51
  363. package/src/commands/console/exec/exec.test.ts +96 -3
  364. package/src/commands/console/exec/exec.ts +59 -6
  365. package/src/commands/console/exec/execute.lua +356 -240
  366. package/src/commands/console/exec/execute.spec.lua +286 -0
  367. package/src/commands/console/logs/logs.test.ts +76 -4
  368. package/src/commands/console/logs/logs.ts +132 -17
  369. package/src/commands/console/logs/query-logs.lua +85 -52
  370. package/src/commands/console/logs/query-logs.spec.lua +132 -0
  371. package/src/commands/explorer/export/export-instances.lua +108 -0
  372. package/src/commands/explorer/export/export-instances.spec.lua +87 -0
  373. package/src/commands/explorer/export/export.test.ts +27 -0
  374. package/src/commands/explorer/export/export.ts +60 -0
  375. package/src/commands/explorer/find/find-instances.lua +174 -0
  376. package/src/commands/explorer/find/find-instances.spec.lua +122 -0
  377. package/src/commands/explorer/find/find.test.ts +53 -0
  378. package/src/commands/explorer/find/find.ts +113 -0
  379. package/src/commands/explorer/grep/grep-scripts.lua +214 -0
  380. package/src/commands/explorer/grep/grep-scripts.spec.lua +130 -0
  381. package/src/commands/explorer/grep/grep.test.ts +45 -0
  382. package/src/commands/explorer/grep/grep.ts +93 -0
  383. package/src/commands/explorer/import/import-instances.lua +106 -0
  384. package/src/commands/explorer/import/import-instances.spec.lua +84 -0
  385. package/src/commands/explorer/import/import.test.ts +60 -0
  386. package/src/commands/explorer/import/import.ts +66 -0
  387. package/src/commands/explorer/query/query-data-model.lua +124 -152
  388. package/src/commands/explorer/query/query-data-model.spec.lua +150 -0
  389. package/src/commands/formatters.test.ts +7 -1
  390. package/src/commands/framework/action-loader.test.ts +28 -0
  391. package/src/commands/framework/action-loader.ts +10 -2
  392. package/src/commands/framework/arg-builder.ts +26 -3
  393. package/src/commands/framework/define-command.ts +32 -3
  394. package/src/commands/framework/index.ts +1 -0
  395. package/src/commands/index.ts +50 -0
  396. package/src/commands/linux/inject-credentials/inject-credentials.ts +1 -3
  397. package/src/commands/linux/setup/setup.ts +1 -3
  398. package/src/commands/linux/status/status.ts +105 -98
  399. package/src/commands/playtest/player/add/add.ts +85 -0
  400. package/src/commands/playtest/player/list/list.ts +57 -0
  401. package/src/commands/playtest/player/playtest-players.test.ts +146 -0
  402. package/src/commands/playtest/player/playtest-players.ts +270 -0
  403. package/src/commands/playtest/player/remove/remove.ts +78 -0
  404. package/src/commands/playtest/playtest-sessions.test.ts +143 -0
  405. package/src/commands/playtest/playtest-sessions.ts +417 -0
  406. package/src/commands/playtest/restart/restart.ts +199 -0
  407. package/src/commands/playtest/run/playtest-runner.luau +264 -0
  408. package/src/commands/playtest/run/playtest-runner.spec.lua +71 -0
  409. package/src/commands/playtest/run/run-playtest-script.lua +139 -0
  410. package/src/commands/playtest/run/run-playtest-script.spec.lua +43 -0
  411. package/src/commands/playtest/run/run.ts +239 -0
  412. package/src/commands/playtest/start/start-playtest.lua +108 -0
  413. package/src/commands/playtest/start/start.ts +138 -0
  414. package/src/commands/playtest/stop/end-playtest.lua +47 -0
  415. package/src/commands/playtest/stop/stop.ts +51 -0
  416. package/src/commands/process/close/close.test.ts +250 -0
  417. package/src/commands/process/close/close.ts +183 -17
  418. package/src/commands/process/info/info.test.ts +198 -44
  419. package/src/commands/process/info/info.ts +204 -15
  420. package/src/commands/process/info/query-state.lua +42 -29
  421. package/src/commands/process/info/query-state.spec.lua +39 -0
  422. package/src/commands/process/launch/launch.test.ts +138 -21
  423. package/src/commands/process/launch/launch.ts +69 -9
  424. package/src/commands/process/list/list.test.ts +1 -0
  425. package/src/commands/process/list/list.ts +14 -6
  426. package/src/commands/process/run/run.ts +26 -5
  427. package/src/commands/serve/serve.test.ts +17 -2
  428. package/src/commands/serve/serve.ts +25 -6
  429. package/src/commands/viewport/screenshot/capture-screenshot.lua +175 -182
  430. package/src/commands/viewport/screenshot/capture-screenshot.spec.lua +129 -0
  431. package/src/commands/viewport/screenshot/screenshot.test.ts +7 -10
  432. package/src/commands/viewport/screenshot/screenshot.ts +6 -1
  433. package/src/deploy/deploy-places.test.ts +127 -0
  434. package/src/deploy/deploy-places.ts +229 -0
  435. package/src/index.ts +11 -0
  436. package/src/plugin/persistent-plugin-installer.test.ts +4 -0
  437. package/src/plugin/persistent-plugin-installer.ts +2 -1
  438. package/src/plugin/plugin-build-version.test.ts +55 -0
  439. package/src/plugin/plugin-build-version.ts +53 -0
  440. package/src/plugin/plugin-injector.ts +7 -1
  441. package/src/process/studio-process-closer.test.ts +107 -0
  442. package/src/process/studio-process-closer.ts +187 -0
  443. package/src/process/studio-settings.test.ts +18 -0
  444. package/src/process/studio-settings.ts +61 -0
  445. package/src/process/studio-window-state.ts +65 -0
  446. package/src/server/studio-bridge-server.ts +15 -4
  447. package/src/server/web-socket-protocol.test.ts +55 -0
  448. package/src/server/web-socket-protocol.ts +468 -8
  449. package/src/test/e2e/persistent-session.test.ts +137 -0
  450. package/src/test/e2e/split-server.test.ts +3 -5
  451. package/templates/studio-bridge-plugin/default.project.json +3 -0
  452. package/templates/studio-bridge-plugin/src/Shared/ActionRouter.lua +208 -159
  453. package/templates/studio-bridge-plugin/src/Shared/ActionRouter.spec.lua +418 -0
  454. package/templates/studio-bridge-plugin/src/Shared/BuiltInActions.lua +94 -0
  455. package/templates/studio-bridge-plugin/src/Shared/BuiltInActions.spec.lua +166 -0
  456. package/templates/studio-bridge-plugin/src/Shared/DiscoveryStateMachine.lua +118 -137
  457. package/templates/studio-bridge-plugin/src/Shared/DiscoveryStateMachine.spec.lua +228 -0
  458. package/templates/studio-bridge-plugin/src/Shared/MessageBuffer.lua +80 -109
  459. package/templates/studio-bridge-plugin/src/Shared/MessageBuffer.spec.lua +116 -0
  460. package/templates/studio-bridge-plugin/src/Shared/StudioBridgeConnection.lua +233 -0
  461. package/templates/studio-bridge-plugin/src/Shared/StudioBridgeConnection.spec.lua +239 -0
  462. package/templates/studio-bridge-plugin/src/Shared/StudioBridgeLogUtils.lua +53 -0
  463. package/templates/studio-bridge-plugin/src/Shared/StudioBridgeLogUtils.spec.lua +27 -0
  464. package/templates/studio-bridge-plugin/src/Shared/StudioBridgeSessionUtils.lua +164 -0
  465. package/templates/studio-bridge-plugin/src/Shared/StudioBridgeSessionUtils.spec.lua +159 -0
  466. package/templates/studio-bridge-plugin/src/StudioBridgePlugin.server.lua +266 -368
  467. package/templates/studio-bridge-plugin/src/Vendor/png.lua +8 -2
  468. package/test/default.project.json +41 -0
  469. package/test/jest.config.lua +3 -0
  470. package/test/scripts/Server/ServerMain.server.lua +10 -0
  471. package/tsconfig.json +1 -0
  472. package/templates/studio-bridge-plugin/src/Shared/Protocol.lua +0 -132
  473. package/templates/studio-bridge-plugin-test/test/actions.test.lua +0 -580
  474. package/templates/studio-bridge-plugin-test/test/discovery.test.lua +0 -622
  475. package/templates/studio-bridge-plugin-test/test/execute-handler.test.lua +0 -517
  476. package/templates/studio-bridge-plugin-test/test/hash-lifecycle.test.lua +0 -814
  477. package/templates/studio-bridge-plugin-test/test/integration.test.lua +0 -1020
  478. package/templates/studio-bridge-plugin-test/test/new-actions.test.lua +0 -339
  479. package/templates/studio-bridge-plugin-test/test/plugin-entry.test.lua +0 -493
  480. package/templates/studio-bridge-plugin-test/test/protocol.test.lua +0 -506
  481. package/templates/studio-bridge-plugin-test/test/roblox-mocks.lua +0 -108
  482. package/templates/studio-bridge-plugin-test/test/test-runner.lua +0 -112
@@ -439,4 +439,141 @@ describe('persistent session e2e', () => {
439
439
  const session = await waitPromise;
440
440
  expect(session.info.sessionId).toBe(plugin.sessionId);
441
441
  });
442
+
443
+ it('client execute is relayed through the host to the plugin', async () => {
444
+ const host = await BridgeConnection.connectAsync({
445
+ port: 0,
446
+ keepAlive: true,
447
+ local: true,
448
+ });
449
+ connections.push(host);
450
+
451
+ const plugin = new MockPluginClient({
452
+ port: host.port,
453
+ instanceId: 'inst-relay',
454
+ capabilities: ['execute', 'syncActions'],
455
+ });
456
+ plugins.push(plugin);
457
+ plugin.autoRespond('syncActions', () => ({
458
+ type: 'syncActionsResult',
459
+ payload: { needed: [], installed: {} },
460
+ }));
461
+ plugin.autoRespond('execute', () => ({
462
+ type: 'scriptComplete',
463
+ payload: { success: true, output: [], returnValues: [42] },
464
+ }));
465
+ await plugin.connectAndRegisterAsync();
466
+ await new Promise((r) => setTimeout(r, 100));
467
+
468
+ const client = await BridgeConnection.connectAsync({
469
+ port: host.port,
470
+ local: true,
471
+ });
472
+ connections.push(client);
473
+ expect(client.role).toBe('client');
474
+
475
+ const session = await client.resolveSessionAsync(plugin.sessionId);
476
+ const result = await session.execAsync('return 42', 2_000);
477
+
478
+ expect(result.success).toBe(true);
479
+ expect(result.returnValues).toEqual([42]);
480
+ });
481
+
482
+ it('host finishes a relayed action before shutting down', async () => {
483
+ const host = await BridgeConnection.connectAsync({
484
+ port: 0,
485
+ keepAlive: true,
486
+ local: true,
487
+ });
488
+ connections.push(host);
489
+
490
+ const plugin = new MockPluginClient({
491
+ port: host.port,
492
+ instanceId: 'inst-drain',
493
+ capabilities: ['execute', 'syncActions'],
494
+ });
495
+ plugins.push(plugin);
496
+ plugin.autoRespond('syncActions', () => ({
497
+ type: 'syncActionsResult',
498
+ payload: { needed: [], installed: {} },
499
+ }));
500
+ plugin.onAnyMessage((msg) => {
501
+ if (msg.type === 'shutdown') {
502
+ void plugin.disconnectAsync();
503
+ return;
504
+ }
505
+ if (msg.type !== 'execute') return;
506
+ setTimeout(() => {
507
+ plugin.sendMessage({
508
+ type: 'scriptComplete',
509
+ sessionId: plugin.sessionId,
510
+ requestId: msg.requestId,
511
+ payload: { success: true, output: [], returnValues: ['late'] },
512
+ });
513
+ }, 300);
514
+ });
515
+ await plugin.connectAndRegisterAsync();
516
+ await new Promise((r) => setTimeout(r, 100));
517
+
518
+ const client = await BridgeConnection.connectAsync({
519
+ port: host.port,
520
+ local: true,
521
+ });
522
+ connections.push(client);
523
+
524
+ const session = await client.resolveSessionAsync(plugin.sessionId);
525
+ const resultPromise = session.execAsync('return "late"', 2_000);
526
+ await new Promise((r) => setTimeout(r, 100));
527
+ const hostShutdown = host.disconnectAsync();
528
+
529
+ const result = await resultPromise;
530
+ await hostShutdown;
531
+
532
+ expect(result.success).toBe(true);
533
+ expect(result.returnValues).toEqual(['late']);
534
+ });
535
+
536
+ it('host stops waiting on a relayed action that never finishes', async () => {
537
+ const host = await BridgeConnection.connectAsync({
538
+ port: 0,
539
+ keepAlive: true,
540
+ local: true,
541
+ });
542
+ connections.push(host);
543
+
544
+ const plugin = new MockPluginClient({
545
+ port: host.port,
546
+ instanceId: 'inst-stuck',
547
+ capabilities: ['execute', 'syncActions'],
548
+ });
549
+ plugins.push(plugin);
550
+ plugin.autoRespond('syncActions', () => ({
551
+ type: 'syncActionsResult',
552
+ payload: { needed: [], installed: {} },
553
+ }));
554
+ plugin.onAnyMessage((msg) => {
555
+ if (msg.type === 'shutdown') {
556
+ void plugin.disconnectAsync();
557
+ }
558
+ });
559
+ await plugin.connectAndRegisterAsync();
560
+ await new Promise((r) => setTimeout(r, 100));
561
+
562
+ const client = await BridgeConnection.connectAsync({
563
+ port: host.port,
564
+ local: true,
565
+ });
566
+ connections.push(client);
567
+
568
+ const session = await client.resolveSessionAsync(plugin.sessionId);
569
+ const stuck = session.execAsync('while true do task.wait() end', 60_000);
570
+ await new Promise((r) => setTimeout(r, 100));
571
+
572
+ const started = Date.now();
573
+ await host.disconnectAsync({ relayDrainTimeoutMs: 200 });
574
+
575
+ expect(Date.now() - started).toBeLessThan(2_000);
576
+ const result = await stuck;
577
+ expect(result.success).toBe(false);
578
+ });
442
579
  });
@@ -4,11 +4,9 @@
4
4
  * that multiple clients can coexist with the host without interfering
5
5
  * with plugin sessions.
6
6
  *
7
- * Note: The current BridgeHost does not implement full host-protocol
8
- * message routing (list-sessions, host-envelope relay). These tests
9
- * exercise the real transport layer and connection lifecycle, not the
10
- * relay protocol. Split-server relay is tested via BridgeConnection
11
- * with a mock host (see bridge-connection-remote.test.ts).
7
+ * These tests exercise the real transport layer and connection lifecycle,
8
+ * not the relay protocol. Relay through a real host is covered in
9
+ * persistent-session.test.ts.
12
10
  */
13
11
 
14
12
  import { describe, it, expect, afterEach } from 'vitest';
@@ -1,5 +1,8 @@
1
1
  {
2
2
  "name": "StudioBridgePlugin",
3
+ "globIgnorePaths": [
4
+ "**/*.spec.lua"
5
+ ],
3
6
  "tree": {
4
7
  "$path": "src"
5
8
  }
@@ -1,164 +1,215 @@
1
- --[[
2
- Action dispatch module for routing incoming protocol messages to handlers.
1
+ --!strict
2
+ --[=[
3
+ Routes incoming studio-bridge messages to handlers by message type, and wraps
4
+ whatever a handler returns into the response message.
3
5
 
4
- Maintains a registry of handler functions keyed by message type. When a
5
- message is dispatched, the router looks up the handler, calls it with
6
- (payload, requestId, sessionId), and constructs a response message if
7
- the handler returns a payload table.
6
+ - An unknown message type answers with an `UNKNOWN_REQUEST` error.
7
+ - A handler that throws answers with an `INTERNAL_ERROR` error.
8
+ - A handler that returns nil sends nothing, which is how an action that
9
+ replies on its own (through `sendMessage`) opts out.
8
10
 
9
- Error handling:
10
- - Unknown message type: returns error response with code UNKNOWN_REQUEST
11
- - Handler throws: returns error response with code INTERNAL_ERROR
12
- - Handler returns nil: no response is generated
11
+ Action modules pushed over the wire call `register` and `setResponseType` on
12
+ the router. Those two keep their lowercase names because a persistent plugin
13
+ installed from an older release still runs action source pushed by a newer CLI.
13
14
 
14
- No Roblox APIs. Pure logic, testable under Lune.
15
- ]]
15
+ @class ActionRouter
16
+ ]=]
16
17
 
17
18
  local ActionRouter = {}
19
+ ActionRouter.ClassName = "ActionRouter"
18
20
  ActionRouter.__index = ActionRouter
19
21
 
20
- -- ---------------------------------------------------------------------------
21
- -- Constructor
22
- -- ---------------------------------------------------------------------------
22
+ export type Message = {
23
+ type: string,
24
+ sessionId: string,
25
+ payload: { [string]: any },
26
+ requestId: string?,
27
+ }
28
+
29
+ export type Handler = (payload: { [string]: any }, requestId: string, sessionId: string) -> { [string]: any }?
30
+
31
+ export type SendMessage = (message: { [string]: any }) -> ()
32
+
33
+ export type ActionInfo = {
34
+ module: any,
35
+ hash: string,
36
+ handlerNames: { string },
37
+ }
38
+
39
+ export type ActionRouter = typeof(setmetatable(
40
+ {} :: {
41
+ _handlers: { [string]: Handler },
42
+ _responseTypes: { [string]: string },
43
+ _actions: { [string]: ActionInfo },
44
+ _vendorPng: any?,
45
+ },
46
+ {} :: typeof({ __index = ActionRouter })
47
+ ))
48
+
49
+ --[=[
50
+ Constructs a new router with no handlers.
51
+ ]=]
52
+ function ActionRouter.new(): ActionRouter
53
+ local self: ActionRouter = setmetatable({} :: any, ActionRouter)
54
+
55
+ self._handlers = {}
56
+ self._responseTypes = {}
57
+ self._actions = {}
58
+
59
+ -- The screenshot action reads its PNG encoder off this field
60
+ self._vendorPng = nil
23
61
 
24
- function ActionRouter.new()
25
- local self = setmetatable({
26
- _handlers = {} :: {
27
- [string]: (payload: { [string]: any }, requestId: string, sessionId: string) -> { [string]: any }?,
28
- },
29
- _responseTypes = {} :: { [string]: string },
30
- _actions = {} :: { [string]: { module: any, hash: string, handlerNames: { string } } },
31
- }, ActionRouter)
32
62
  return self
33
63
  end
34
64
 
35
- -- ---------------------------------------------------------------------------
36
- -- Register a handler for a message type
37
- -- ---------------------------------------------------------------------------
38
-
39
- function ActionRouter.register(
40
- self: any,
41
- messageType: string,
42
- handler: (
43
- payload: { [string]: any },
44
- requestId: string,
45
- sessionId: string
46
- ) -> { [string]: any }?
47
- )
65
+ --[=[
66
+ Sets the handler for a message type, replacing any previous one.
67
+ ]=]
68
+ function ActionRouter.register(self: ActionRouter, messageType: string, handler: Handler)
48
69
  self._handlers[messageType] = handler
49
70
  end
50
71
 
51
- -- ---------------------------------------------------------------------------
52
- -- Dispatch an incoming message
53
- -- ---------------------------------------------------------------------------
54
-
55
- --[[
56
- Dispatch an incoming message to the appropriate handler.
57
-
58
- @param message Table with fields: type, sessionId, payload, requestId?
59
- @return A response message table, or nil if the handler returns nil.
60
- ]]
61
- function ActionRouter.dispatch(
62
- self: any,
63
- message: {
64
- type: string,
65
- sessionId: string,
66
- payload: { [string]: any },
67
- requestId: string?,
68
- }
69
- ): { [string]: any }?
70
- local handler = self._handlers[message.type]
72
+ --[=[
73
+ Overrides the response type for a message type. Defaults to `<messageType>Result`.
74
+ ]=]
75
+ function ActionRouter.setResponseType(self: ActionRouter, messageType: string, responseType: string)
76
+ self._responseTypes[messageType] = responseType
77
+ end
78
+
79
+ --[=[
80
+ Sets the PNG encoder handed to the screenshot action.
81
+ ]=]
82
+ function ActionRouter.SetVendorPng(self: ActionRouter, png: any)
83
+ self._vendorPng = png
84
+ end
71
85
 
86
+ --[=[
87
+ Dispatches a message to its handler and returns the response message, if any.
88
+ ]=]
89
+ function ActionRouter.Dispatch(self: ActionRouter, message: Message): Message?
90
+ local handler = self._handlers[message.type]
72
91
  if handler == nil then
73
- return {
74
- type = "error",
75
- sessionId = message.sessionId,
76
- requestId = message.requestId,
77
- payload = {
78
- code = "UNKNOWN_REQUEST",
79
- message = "Unknown message type: " .. tostring(message.type),
80
- },
81
- }
92
+ return ActionRouter._errorResponse(message, "UNKNOWN_REQUEST", `Unknown message type: {tostring(message.type)}`)
82
93
  end
83
94
 
84
- local requestId = message.requestId or ""
85
- local ok, result = pcall(handler, message.payload, requestId, message.sessionId)
86
-
95
+ local ok, result = pcall(handler, message.payload, message.requestId or "", message.sessionId)
87
96
  if not ok then
88
- return {
89
- type = "error",
90
- sessionId = message.sessionId,
91
- requestId = message.requestId,
92
- payload = {
93
- code = "INTERNAL_ERROR",
94
- message = "Handler error: " .. tostring(result),
95
- },
96
- }
97
+ return ActionRouter._errorResponse(message, "INTERNAL_ERROR", `Handler error: {tostring(result)}`)
97
98
  end
98
99
 
99
100
  if result == nil then
100
101
  return nil
101
102
  end
102
103
 
103
- -- Construct response with the appropriate response type
104
- local responseType = self._responseTypes[message.type] or (message.type .. "Result")
104
+ if result.success == false and type(result.code) == "string" and type(result.message) == "string" then
105
+ return ActionRouter._errorResponse(message, result.code, result.message)
106
+ end
105
107
 
106
108
  return {
107
- type = responseType,
109
+ type = self._responseTypes[message.type] or `{message.type}Result`,
108
110
  sessionId = message.sessionId,
109
111
  requestId = message.requestId,
110
112
  payload = result,
111
113
  }
112
114
  end
113
115
 
114
- -- ---------------------------------------------------------------------------
115
- -- Set a custom response type for a message type
116
- -- ---------------------------------------------------------------------------
116
+ --[=[
117
+ Tears down every installed action and drops all handlers.
118
+ ]=]
119
+ function ActionRouter.Destroy(self: ActionRouter)
120
+ for _, action in self._actions do
121
+ if type(action.module) == "table" and type(action.module.teardown) == "function" then
122
+ pcall(action.module.teardown)
123
+ end
124
+ end
117
125
 
118
- function ActionRouter.setResponseType(self: any, messageType: string, responseType: string)
119
- self._responseTypes[messageType] = responseType
126
+ table.clear(self._actions)
127
+ table.clear(self._handlers)
128
+ table.clear(self._responseTypes)
129
+ end
130
+
131
+ --[=[
132
+ Compiles Luau source into a function, like `loadstring`. A playtest's client
133
+ has no `loadstring`, so there the source compiles through a ModuleScript
134
+ instead. Returns nil and the compile error when the source does not compile.
135
+ ]=]
136
+ function ActionRouter.Compile(_self: ActionRouter, source: string, chunkName: string?): (any, string?)
137
+ local loadstringOk, fn, compileErr = pcall(loadstring :: any, source, chunkName)
138
+ if loadstringOk then
139
+ return fn, compileErr
140
+ end
141
+
142
+ return ActionRouter._compileWithModuleScript(source, chunkName)
143
+ end
144
+
145
+ function ActionRouter._compileWithModuleScript(source: string, chunkName: string?): (any, string?)
146
+ local moduleName = if chunkName then string.gsub(chunkName, "^[@=]", "") else "compiled"
147
+
148
+ local moduleScript = Instance.new("ModuleScript")
149
+ moduleScript.Name = moduleName
150
+ -- Wrapping on the first line keeps a top-level `return` legal and line numbers unchanged
151
+ moduleScript.Source = `return function(...) {source}\nend`
152
+
153
+ -- require only reports that the module failed to load; the compile error
154
+ -- itself reaches the output a frame later
155
+ local compileErrors: { string } = {}
156
+ local logConnection = game:GetService("LogService").MessageOut:Connect(function(message, messageType)
157
+ if messageType == Enum.MessageType.MessageError and string.find(message, moduleName, 1, true) == 1 then
158
+ table.insert(compileErrors, message)
159
+ end
160
+ end)
161
+
162
+ local requireOk, result = pcall(require, moduleScript)
163
+ if not requireOk then
164
+ task.wait()
165
+ end
166
+
167
+ logConnection:Disconnect()
168
+ moduleScript:Destroy()
169
+
170
+ if requireOk then
171
+ return result, nil
172
+ end
173
+ return nil, compileErrors[1] or tostring(result)
174
+ end
175
+
176
+ function ActionRouter._errorResponse(message: Message, code: string, errorMessage: string): Message
177
+ return {
178
+ type = "error",
179
+ sessionId = message.sessionId,
180
+ requestId = message.requestId,
181
+ payload = {
182
+ code = code,
183
+ message = errorMessage,
184
+ },
185
+ }
120
186
  end
121
187
 
122
- -- ---------------------------------------------------------------------------
123
- -- Register a dynamic action from source code
124
- -- ---------------------------------------------------------------------------
125
-
126
- --[[
127
- Dynamically register an action from Luau source code received over the wire.
128
-
129
- The source is expected to be a module that returns a table with a `register`
130
- function: `function(router, sendMessage?, logBuffer?)`. This mirrors the
131
- static action module convention.
132
-
133
- If the source defines a simple handler function instead, it is registered
134
- directly as the handler for the given action name.
135
-
136
- @param name The action name (used as the message type if not registered otherwise).
137
- @param source The Luau source code string.
138
- @param sendMessage Optional callback for actions that send their own responses.
139
- @param logBuffer Optional log buffer instance.
140
- @param responseType Optional response type override.
141
- @param hash Optional content hash for skip-on-same-hash optimization.
142
- @return success, error?
143
- ]]
144
- function ActionRouter.registerAction(
145
- self: any,
188
+ --[=[
189
+ Installs an action from Luau source received over the wire.
190
+
191
+ The source returns either a table with `register(router, sendMessage?, logBuffer?)`
192
+ (and optionally `teardown()`), or a bare handler function registered under `name`.
193
+
194
+ Re-registering a name tears the previous module down and removes its handlers
195
+ first. With a `hash`, the same hash installed twice is a no-op.
196
+ ]=]
197
+ function ActionRouter.RegisterAction(
198
+ self: ActionRouter,
146
199
  name: string,
147
200
  source: string,
148
- sendMessage: ((msg: { [string]: any }) -> ())?,
201
+ sendMessage: SendMessage?,
149
202
  logBuffer: any?,
150
203
  responseType: string?,
151
204
  hash: string?
152
205
  ): (boolean, string?)
153
- -- Hash-based skip: if the same hash is already installed, skip re-registration
154
- if hash and self._actions[name] and self._actions[name].hash == hash then
206
+ local oldAction = self._actions[name]
207
+ if hash and oldAction and oldAction.hash == hash then
155
208
  return true, nil
156
209
  end
157
210
 
158
- -- If an old module exists, tear it down and remove its handlers
159
- local oldAction = self._actions[name]
160
211
  if oldAction then
161
- if type(oldAction.module.teardown) == "function" then
212
+ if type(oldAction.module) == "table" and type(oldAction.module.teardown) == "function" then
162
213
  pcall(oldAction.module.teardown)
163
214
  end
164
215
  for _, handlerName in oldAction.handlerNames do
@@ -167,77 +218,75 @@ function ActionRouter.registerAction(
167
218
  end
168
219
  end
169
220
 
170
- -- Snapshot handler keys before registration
171
- local beforeHandlers = {}
172
- for k, _ in self._handlers do
173
- beforeHandlers[k] = true
221
+ local handlersBefore: { [string]: boolean } = {}
222
+ for handlerName in self._handlers do
223
+ handlersBefore[handlerName] = true
174
224
  end
175
225
 
176
- -- Load the source code
177
- local loadOk, moduleOrErr = pcall(function()
178
- return (loadstring :: any)(source, `@action/{name}`)
179
- end)
180
-
181
- if not loadOk or not moduleOrErr then
182
- return false, `Failed to load action source: {moduleOrErr or "loadstring returned nil"}`
226
+ local loadOk, chunkOrErr, compileErr = pcall(self.Compile, self, source, `@action/{name}`)
227
+ if not loadOk or not chunkOrErr then
228
+ return false, `Failed to load action source: {compileErr or chunkOrErr or "compile returned nil"}`
183
229
  end
184
230
 
185
- -- Execute the loaded chunk to get the module table
186
- local execOk, moduleTable = pcall(moduleOrErr)
231
+ local execOk, moduleOrErr = pcall(chunkOrErr)
187
232
  if not execOk then
188
- return false, `Failed to execute action module: {moduleTable}`
233
+ return false, `Failed to execute action module: {moduleOrErr}`
189
234
  end
190
235
 
191
- -- If the module has a register function, call it
192
- if type(moduleTable) == "table" and type(moduleTable.register) == "function" then
193
- local regOk, regErr = pcall(moduleTable.register, self, sendMessage, logBuffer)
194
- if not regOk then
195
- return false, `register() failed: {regErr}`
236
+ if type(moduleOrErr) == "table" and type(moduleOrErr.register) == "function" then
237
+ local registerOk, registerErr = pcall(moduleOrErr.register, self, sendMessage, logBuffer)
238
+ if not registerOk then
239
+ return false, `register() failed: {registerErr}`
196
240
  end
197
- elseif type(moduleTable) == "function" then
198
- -- Simple handler function
199
- self:register(name, moduleTable)
241
+ elseif type(moduleOrErr) == "function" then
242
+ self:register(name, moduleOrErr)
200
243
  else
201
- return false, `Action module must return a table with .register() or a function`
244
+ return false, "Action module must return a table with .register() or a function"
202
245
  end
203
246
 
204
- -- Set custom response type if provided
205
247
  if responseType then
206
248
  self._responseTypes[name] = responseType
207
249
  end
208
250
 
209
- -- Diff handler keys to find newly registered handler names
210
- local registeredNames = {}
211
- for k, _ in self._handlers do
212
- if not beforeHandlers[k] then
213
- table.insert(registeredNames, k)
251
+ local handlerNames: { string } = {}
252
+ for handlerName in self._handlers do
253
+ if not handlersBefore[handlerName] then
254
+ table.insert(handlerNames, handlerName)
214
255
  end
215
256
  end
216
257
 
217
- -- Store in _actions for hash tracking
218
258
  if hash then
219
259
  self._actions[name] = {
220
- module = moduleTable,
260
+ module = moduleOrErr,
221
261
  hash = hash,
222
- handlerNames = registeredNames,
262
+ handlerNames = handlerNames,
223
263
  }
224
264
  end
225
265
 
226
266
  return true, nil
227
267
  end
228
268
 
229
- -- ---------------------------------------------------------------------------
230
- -- Get installed actions with their hashes
231
- -- ---------------------------------------------------------------------------
232
-
233
- --[[
234
- Returns a map of action name -> content hash for all installed actions.
235
- Used by the syncActions protocol to determine which actions need updating.
236
- ]]
237
- function ActionRouter.getInstalledActions(self: any): { [string]: string }
238
- local result = {}
239
- for name, info in self._actions do
240
- result[name] = info.hash
269
+ --[=[
270
+ Returns the handler names an installed action registered, or nil when no
271
+ action with a hash is installed under `name`.
272
+ ]=]
273
+ function ActionRouter.GetActionHandlerNames(self: ActionRouter, name: string): { string }?
274
+ local action = self._actions[name]
275
+ if not action then
276
+ return nil
277
+ end
278
+
279
+ return table.clone(action.handlerNames)
280
+ end
281
+
282
+ --[=[
283
+ Returns action name to content hash for every installed action. The bridge
284
+ compares this against its own hashes to decide which actions to push.
285
+ ]=]
286
+ function ActionRouter.GetInstalledActions(self: ActionRouter): { [string]: string }
287
+ local result: { [string]: string } = {}
288
+ for name, action in self._actions do
289
+ result[name] = action.hash
241
290
  end
242
291
  return result
243
292
  end