@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
@@ -1,317 +1,433 @@
1
- --[[
2
- Execute action handler for the studio-bridge plugin.
3
-
4
- Compiles and runs Luau code received from the server, returning a
5
- scriptComplete response on success or an error response on failure.
6
-
7
- Supports:
8
- - requestId correlation: if present in the request, echoed in all
9
- response messages (scriptComplete and output).
10
- - Distinct error codes: SCRIPT_LOAD_ERROR, SCRIPT_RUNTIME_ERROR.
11
- - Sequential queueing: concurrent execute requests are processed
12
- one at a time in FIFO order.
13
- - Return values: everything the script returns is marshalled onto
14
- `payload.returnValues`, so a caller can read a result as a value
15
- instead of scraping it back out of printed output.
16
-
17
- This module has no Roblox dependencies and is testable under Lune.
18
- ]]
1
+ --!strict
2
+ --[=[
3
+ Action that compiles and runs Luau code sent by the bridge, answering with a
4
+ `scriptComplete` message.
5
+
6
+ - Echoes the request's `requestId` on every response.
7
+ - Fails with `SCRIPT_LOAD_ERROR` when the code does not compile,
8
+ `SCRIPT_RUNTIME_ERROR` with a `traceback` when it throws, and
9
+ `SCRIPT_TIMEOUT` when it outlives `payload.timeoutMs`. A timed out script
10
+ is cancelled, so the requests queued behind it still run.
11
+ - Passes `payload.args`, a list of strings, to the script as `...`.
12
+ - Runs concurrent requests one at a time, in arrival order.
13
+ - Returns everything printed while the script runs, read from LogService, so
14
+ output from required modules and from threads the script starts comes back.
15
+ - Marshals everything the script returns onto `payload.returnValues`, so a
16
+ caller reads a result as a value instead of scraping it out of the output.
17
+
18
+ Loaded standalone inside the plugin, so it cannot require anything.
19
+
20
+ Compiles through the router's `Compile`, which still works in a playtest's
21
+ client where `loadstring` does not exist. Routers from older plugins have no
22
+ `Compile`, and fall back to `loadstring`.
23
+
24
+ @class ExecuteAction
25
+ ]=]
19
26
 
20
- local ExecuteAction = {}
27
+ local MAX_DEPTH = 64
28
+ local CHUNK_NAME = "=exec"
29
+ local PLUGIN_PREFIX = "[StudioBridge]"
30
+ -- Tracebacks name this module's frames the way its first line does
31
+ local OWN_FRAME_PREFIX = string.match(debug.traceback(), "^([^\n]-):%d+")
21
32
 
22
- -- ---------------------------------------------------------------------------
23
- -- Internal queue for sequential execution
24
- -- ---------------------------------------------------------------------------
33
+ local ExecuteAction = {}
25
34
 
26
- local _queue: { { payload: { [string]: any }, requestId: string?, sessionId: string, sendMessage: ((msg: { [string]: any }) -> ())? } } =
27
- {}
28
- local _processing = false
35
+ type SendMessage = (message: { [string]: any }) -> ()
29
36
 
30
- -- ---------------------------------------------------------------------------
31
- -- Return value serialization
32
- -- ---------------------------------------------------------------------------
37
+ type QueuedRequest = {
38
+ payload: { [string]: any },
39
+ requestId: string?,
40
+ sessionId: string,
41
+ sendMessage: SendMessage?,
42
+ receivedAt: number,
43
+ }
44
+
45
+ type LogEntry = {
46
+ level: string,
47
+ body: string,
48
+ timestamp: number,
49
+ }
50
+
51
+ local LOG_LEVELS: { [Enum.MessageType]: string } = {
52
+ [Enum.MessageType.MessageOutput] = "Print",
53
+ [Enum.MessageType.MessageInfo] = "Info",
54
+ [Enum.MessageType.MessageWarning] = "Warning",
55
+ [Enum.MessageType.MessageError] = "Error",
56
+ }
57
+
58
+ local queue: { QueuedRequest } = {}
59
+ local isProcessing = false
60
+ local compile: (source: string, chunkName: string?) -> (any, string?) = loadstring :: any
61
+
62
+ --[=[
63
+ Registers the `execute` handler. With `sendMessage`, requests queue and each
64
+ reply is sent directly; without it, the reply goes back through the router.
65
+ ]=]
66
+ function ExecuteAction.register(router: any, sendMessage: SendMessage?)
67
+ if type(router.Compile) == "function" then
68
+ compile = function(source: string, chunkName: string?)
69
+ return router:Compile(source, chunkName)
70
+ end
71
+ end
33
72
 
34
- -- Produces the SerializedReturnValue shapes declared in
35
- -- web-socket-protocol.ts. Action modules are loadstring'd standalone inside
36
- -- the plugin and cannot require anything, so this cannot be shared with the
37
- -- equivalent marshaller in query-data-model.lua.
73
+ router:setResponseType("execute", "scriptComplete")
74
+ router:register("execute", function(payload: { [string]: any }, requestId: string, sessionId: string)
75
+ if not sendMessage then
76
+ return ExecuteAction._handleExecute(payload, requestId, sessionId, nil)
77
+ end
38
78
 
39
- local MAX_DEPTH = 64
79
+ table.insert(queue, {
80
+ payload = payload,
81
+ requestId = requestId,
82
+ sessionId = sessionId,
83
+ sendMessage = sendMessage,
84
+ receivedAt = os.clock(),
85
+ })
86
+ ExecuteAction._processQueue()
40
87
 
41
- local function unsupported(typeName: string, text: string): { [string]: any }
42
- return { type = "Unsupported", typeName = typeName, toString = text }
88
+ -- Replied through sendMessage, so the router must not reply as well
89
+ return nil
90
+ end)
43
91
  end
44
92
 
45
- local serializeValue: (value: any, depth: number, seen: { [any]: boolean }) -> any
46
-
47
- -- An array-like table becomes a JSON array, anything else a JSON object with
48
- -- string keys. Mixed tables take the object branch on purpose: JSONEncode
49
- -- rejects them, and a rejected encode drops the whole scriptComplete message.
50
- local function serializeTable(value: { [any]: any }, depth: number, seen: { [any]: boolean }): any
51
- local count = 0
52
- for _ in value do
53
- count += 1
54
- end
55
-
56
- if count == #value then
57
- local array = {}
58
- for index = 1, count do
59
- array[index] = serializeValue(value[index], depth + 1, seen)
93
+ --[=[
94
+ Cancels every queued request, replying `ACTION_REPLACED` to each.
95
+ ]=]
96
+ function ExecuteAction.teardown()
97
+ for _, request in queue do
98
+ if request.sendMessage then
99
+ request.sendMessage({
100
+ type = "scriptComplete",
101
+ sessionId = request.sessionId,
102
+ requestId = request.requestId,
103
+ payload = {
104
+ success = false,
105
+ error = "Action re-registered, request cancelled",
106
+ code = "ACTION_REPLACED",
107
+ },
108
+ })
60
109
  end
61
- return array
62
110
  end
63
111
 
64
- local map: { [string]: any } = {}
65
- for key, item in value do
66
- map[if type(key) == "string" then key else tostring(key)] = serializeValue(item, depth + 1, seen)
67
- end
68
- return map
112
+ ExecuteAction._resetQueue()
69
113
  end
70
114
 
71
- function serializeValue(value: any, depth: number, seen: { [any]: boolean }): any
72
- local valueType = typeof(value)
115
+ --[=[
116
+ Runs an execute request and returns its response payload, bypassing the queue.
117
+ ]=]
118
+ function ExecuteAction.handleExecute(
119
+ payload: { [string]: any },
120
+ requestId: string?,
121
+ sessionId: string
122
+ ): { [string]: any }?
123
+ return ExecuteAction._handleExecute(payload, requestId, sessionId, nil)
124
+ end
73
125
 
74
- if valueType == "nil" or valueType == "string" or valueType == "boolean" then
75
- return value
76
- elseif valueType == "number" then
77
- -- inf and nan have no JSON spelling, and encoding one produces a
78
- -- document the server cannot decode -- losing the entire message.
79
- if value ~= value or value == math.huge or value == -math.huge then
80
- return unsupported("number", tostring(value))
81
- end
82
- return value
83
- elseif valueType == "table" then
84
- if depth > MAX_DEPTH then
85
- return unsupported("table", "<max depth exceeded>")
86
- end
87
- if seen[value] then
88
- return unsupported("table", "<cycle>")
89
- end
90
- seen[value] = true
91
- local serialized = serializeTable(value, depth, seen)
92
- seen[value] = nil
93
- return serialized
94
- elseif valueType == "Vector3" then
95
- return { type = "Vector3", value = { value.X, value.Y, value.Z } }
96
- elseif valueType == "Vector2" then
97
- return { type = "Vector2", value = { value.X, value.Y } }
98
- elseif valueType == "CFrame" then
99
- return { type = "CFrame", value = { value:GetComponents() } }
100
- elseif valueType == "Color3" then
101
- return { type = "Color3", value = { value.R, value.G, value.B } }
102
- elseif valueType == "UDim2" then
103
- return { type = "UDim2", value = { value.X.Scale, value.X.Offset, value.Y.Scale, value.Y.Offset } }
104
- elseif valueType == "UDim" then
105
- return { type = "UDim", value = { value.Scale, value.Offset } }
106
- elseif valueType == "BrickColor" then
107
- return { type = "BrickColor", name = value.Name, value = value.Number }
108
- elseif valueType == "EnumItem" then
109
- return { type = "EnumItem", enum = tostring(value.EnumType), name = value.Name, value = value.Value }
110
- elseif valueType == "Instance" then
111
- return { type = "Instance", className = value.ClassName, path = value:GetFullName() }
112
- else
113
- return unsupported(valueType, tostring(value))
114
- end
126
+ function ExecuteAction._resetQueue()
127
+ queue = {}
128
+ isProcessing = false
115
129
  end
116
130
 
117
- -- Marshal the values a script returned, in order. A returned nil becomes an
118
- -- explicit marker: a JSON array cannot hold a hole, and dropping the entry
119
- -- would shift every later value into the wrong position.
120
- local function serializeReturnValues(packed: { n: number, [number]: any }): { any }
121
- local returnValues = {}
122
- for index = 2, packed.n do
123
- local serialized = serializeValue(packed[index], 1, {})
124
- if serialized == nil then
125
- serialized = { type = "Nil" }
126
- end
127
- returnValues[index - 1] = serialized
131
+ function ExecuteAction._processQueue()
132
+ if isProcessing then
133
+ return
134
+ end
135
+ isProcessing = true
136
+
137
+ while #queue > 0 do
138
+ local request = table.remove(queue, 1) :: QueuedRequest
139
+ ExecuteAction._handleExecute(
140
+ request.payload,
141
+ request.requestId,
142
+ request.sessionId,
143
+ request.sendMessage,
144
+ request.receivedAt
145
+ )
128
146
  end
129
- return returnValues
130
- end
131
147
 
132
- -- ---------------------------------------------------------------------------
133
- -- Core execution logic
134
- -- ---------------------------------------------------------------------------
148
+ isProcessing = false
149
+ end
135
150
 
136
- -- Handle an execute request by compiling and running the provided code.
137
- -- Captures print/warn output by temporarily intercepting the globals.
138
151
  function ExecuteAction._handleExecute(
139
152
  payload: { [string]: any },
140
153
  requestId: string?,
141
154
  sessionId: string,
142
- sendMessage: ((msg: { [string]: any }) -> ())?
155
+ sendMessage: SendMessage?,
156
+ receivedAt: number?
143
157
  ): { [string]: any }?
144
- local code = payload and (payload.script or payload.code)
158
+ -- An empty requestId means none was sent
159
+ local effectiveRequestId = if requestId ~= "" then requestId else nil
145
160
 
146
- -- Normalize requestId: treat empty string as absent (v1 compatibility)
147
- local effectiveRequestId: string? = nil
148
- if requestId ~= nil and requestId ~= "" then
149
- effectiveRequestId = requestId
150
- end
161
+ local function reply(responsePayload: { [string]: any }): { [string]: any }?
162
+ if not sendMessage then
163
+ return responsePayload
164
+ end
151
165
 
152
- -- Helper to build response messages with optional requestId
153
- local function buildResponse(responsePayload: { [string]: any }): { [string]: any }
154
- local msg: { [string]: any } = {
166
+ sendMessage({
155
167
  type = "scriptComplete",
156
168
  sessionId = sessionId,
169
+ requestId = effectiveRequestId,
157
170
  payload = responsePayload,
158
- }
159
- if effectiveRequestId then
160
- msg.requestId = effectiveRequestId
161
- end
162
- return msg
163
- end
164
-
165
- -- Helper to send or return the result
166
- local function sendResult(responsePayload: { [string]: any }): { [string]: any }?
167
- if sendMessage then
168
- sendMessage(buildResponse(responsePayload))
169
- return nil
170
- end
171
- return responsePayload
171
+ })
172
+ return nil
172
173
  end
173
174
 
174
- if not code or type(code) ~= "string" then
175
- return sendResult({ success = false, error = "Missing code in execute request", code = "SCRIPT_LOAD_ERROR" })
175
+ local code = payload and (payload.script or payload.code)
176
+ if type(code) ~= "string" then
177
+ return reply({ success = false, error = "Missing code in execute request", code = "SCRIPT_LOAD_ERROR" })
176
178
  end
177
179
 
178
- local compileOk, fnOrErr = pcall(loadstring, code)
180
+ local compileOk, fn, compileErr = pcall(compile, code, CHUNK_NAME)
179
181
  if not compileOk then
180
- return sendResult({ success = false, error = tostring(fnOrErr), code = "SCRIPT_LOAD_ERROR" })
182
+ return reply({ success = false, error = tostring(fn), code = "SCRIPT_LOAD_ERROR" })
181
183
  end
182
- if not fnOrErr then
183
- return sendResult({ success = false, error = "Failed to compile script", code = "SCRIPT_LOAD_ERROR" })
184
+ if not fn then
185
+ return reply({
186
+ success = false,
187
+ error = if compileErr then tostring(compileErr) else "Failed to compile script",
188
+ code = "SCRIPT_LOAD_ERROR",
189
+ })
184
190
  end
185
191
 
186
- local fn = fnOrErr
187
-
188
- -- Capture output by temporarily intercepting print/warn in the global
189
- -- environment. Local variable shadowing doesn't work because loadstring'd
190
- -- code accesses the global environment, not this module's locals.
191
- local captured: { { level: string, body: string, timestamp: number } } = {}
192
- local env = getfenv(fn)
193
- local originalPrint = env.print
194
- local originalWarn = env.warn
195
-
196
- env.print = function(...)
197
- local parts = {}
198
- for i = 1, select("#", ...) do
199
- parts[i] = tostring(select(i, ...))
200
- end
201
- local body = table.concat(parts, "\t")
202
- table.insert(captured, { level = "Print", body = body, timestamp = os.clock() })
203
- originalPrint(...)
192
+ local args = payload.args or {}
193
+ if type(args) ~= "table" then
194
+ return reply({ success = false, error = "Script args must be a list", code = "SCRIPT_LOAD_ERROR" })
204
195
  end
205
196
 
206
- env.warn = function(...)
207
- local parts = {}
208
- for i = 1, select("#", ...) do
209
- parts[i] = tostring(select(i, ...))
197
+ local timeoutMs = payload.timeoutMs
198
+ local timeoutSeconds = if type(timeoutMs) == "number" then timeoutMs / 1000 else nil
199
+ -- The caller's timeout started when it sent the request, not when its turn came
200
+ if timeoutSeconds and receivedAt then
201
+ timeoutSeconds -= os.clock() - receivedAt
202
+ if timeoutSeconds <= 0 then
203
+ return reply({
204
+ success = false,
205
+ error = `Script timed out after {timeoutMs}ms waiting behind other scripts, and never ran`,
206
+ code = "SCRIPT_TIMEOUT",
207
+ })
210
208
  end
211
- local body = table.concat(parts, "\t")
212
- table.insert(captured, { level = "Warning", body = body, timestamp = os.clock() })
213
- originalWarn(...)
214
209
  end
215
210
 
216
- local returned = table.pack(pcall(fn))
217
- local success = returned[1]
211
+ local captured: { LogEntry } = {}
212
+ local logConnection = ExecuteAction._captureOutput(captured)
213
+ local status, result = ExecuteAction._runAsync(fn, timeoutSeconds, args)
218
214
 
219
- -- Restore originals
220
- env.print = originalPrint
221
- env.warn = originalWarn
215
+ -- MessageOut can fire a frame late, after the script's last print
216
+ task.wait()
217
+ logConnection:Disconnect()
222
218
 
223
- if not success then
224
- return sendResult({
219
+ if status == "timeout" then
220
+ return reply({
225
221
  success = false,
226
- error = tostring(returned[2]),
222
+ error = `Script timed out after {timeoutMs}ms and was cancelled`,
223
+ code = "SCRIPT_TIMEOUT",
224
+ output = captured,
225
+ })
226
+ elseif status == "error" then
227
+ return reply({
228
+ success = false,
229
+ error = result.message,
230
+ traceback = result.traceback,
227
231
  code = "SCRIPT_RUNTIME_ERROR",
228
232
  output = captured,
229
233
  })
230
234
  end
231
235
 
232
- return sendResult({
236
+ return reply({
233
237
  success = true,
234
238
  output = captured,
235
- returnValues = serializeReturnValues(returned),
239
+ returnValues = ExecuteAction._serializeReturnValues(result),
236
240
  })
237
241
  end
238
242
 
239
- -- ---------------------------------------------------------------------------
240
- -- Queue processing
241
- -- ---------------------------------------------------------------------------
242
-
243
- local function _processQueue()
244
- if _processing then
245
- return
243
+ --[[
244
+ Drops the frames of this module's own wrapper from a traceback, leaving only
245
+ the script's.
246
+ ]]
247
+ function ExecuteAction._dropOwnFrames(traceback: string): string
248
+ if not OWN_FRAME_PREFIX then
249
+ return traceback
246
250
  end
247
- _processing = true
248
251
 
249
- while #_queue > 0 do
250
- local item = table.remove(_queue, 1)
251
- ExecuteAction._handleExecute(item.payload, item.requestId, item.sessionId, item.sendMessage)
252
+ local lines = {}
253
+ for _, line in string.split(traceback, "\n") do
254
+ if string.sub(line, 1, #OWN_FRAME_PREFIX + 1) ~= `{OWN_FRAME_PREFIX}:` then
255
+ table.insert(lines, line)
256
+ end
252
257
  end
258
+ return table.concat(lines, "\n")
259
+ end
253
260
 
254
- _processing = false
261
+ --[[
262
+ Collects every message logged until the connection is disconnected, other
263
+ than the plugin's own.
264
+ ]]
265
+ function ExecuteAction._captureOutput(captured: { LogEntry }): RBXScriptConnection
266
+ return game:GetService("LogService").MessageOut:Connect(function(message: string, messageType: Enum.MessageType)
267
+ if string.sub(message, 1, #PLUGIN_PREFIX) == PLUGIN_PREFIX then
268
+ return
269
+ end
270
+
271
+ table.insert(captured, {
272
+ level = LOG_LEVELS[messageType] or "Print",
273
+ body = message,
274
+ timestamp = os.clock(),
275
+ })
276
+ end)
255
277
  end
256
278
 
257
- -- ---------------------------------------------------------------------------
258
- -- Public API
259
- -- ---------------------------------------------------------------------------
279
+ --[[
280
+ Runs `fn` with `args` in its own thread. Returns "ok" and everything it returned
281
+ (pcall-style, status first), "error" and its message and traceback, or
282
+ "timeout" once `timeoutSec` passes, after cancelling the thread.
283
+ ]]
284
+ function ExecuteAction._runAsync(fn: (...any) -> ...any, timeoutSec: number?, args: { any }?): (string, any)
285
+ local callerThread = coroutine.running()
286
+ local isWaiting = false
287
+ local outcome: { status: string, result: any }? = nil
288
+
289
+ local function finish(status: string, result: any)
290
+ if outcome then
291
+ return
292
+ end
293
+ outcome = { status = status, result = result }
294
+ if isWaiting then
295
+ task.defer(callerThread)
296
+ end
297
+ end
260
298
 
261
- -- Register this handler with the ActionRouter.
262
- function ExecuteAction.register(router: any, sendMessage: ((msg: { [string]: any }) -> ())?)
263
- router:setResponseType("execute", "scriptComplete")
264
- router:register("execute", function(payload: { [string]: any }, requestId: string, sessionId: string)
265
- if sendMessage then
266
- -- Queue the request for sequential processing
267
- table.insert(_queue, {
268
- payload = payload,
269
- requestId = requestId,
270
- sessionId = sessionId,
271
- sendMessage = sendMessage,
272
- })
273
- _processQueue()
274
- -- Return nil so the ActionRouter does not generate a response
275
- return nil
299
+ local scriptThread = task.spawn(function()
300
+ local packed = table.pack(xpcall(fn, function(err)
301
+ return {
302
+ message = tostring(err),
303
+ traceback = ExecuteAction._dropOwnFrames(debug.traceback(nil, 2)),
304
+ }
305
+ end, table.unpack(args or {})))
306
+ if packed[1] then
307
+ finish("ok", packed)
276
308
  else
277
- -- Direct mode (no sendMessage): return payload for ActionRouter wrapping
278
- return ExecuteAction._handleExecute(payload, requestId, sessionId, nil)
309
+ finish("error", packed[2])
279
310
  end
280
311
  end)
312
+
313
+ if not outcome then
314
+ local timeoutThread = if timeoutSec
315
+ then task.delay(timeoutSec, function()
316
+ if not outcome then
317
+ pcall(task.cancel, scriptThread)
318
+ finish("timeout", nil)
319
+ end
320
+ end)
321
+ else nil
322
+
323
+ isWaiting = true
324
+ coroutine.yield()
325
+ if timeoutThread then
326
+ pcall(task.cancel, timeoutThread)
327
+ end
328
+ end
329
+
330
+ local final = outcome :: { status: string, result: any }
331
+ return final.status, final.result
281
332
  end
282
333
 
283
- -- Handle an execute request directly (for testing without ActionRouter).
284
- function ExecuteAction.handleExecute(
285
- payload: { [string]: any },
286
- requestId: string?,
287
- sessionId: string
288
- ): { [string]: any }?
289
- return ExecuteAction._handleExecute(payload, requestId, sessionId, nil)
334
+ --[[
335
+ Marshals what a script returned, in order, skipping the pcall status. A nil
336
+ becomes an explicit marker: a JSON array cannot hold a hole, and dropping it
337
+ would shift every later value into the wrong position.
338
+ ]]
339
+ function ExecuteAction._serializeReturnValues(packed: { n: number, [number]: any }): { any }
340
+ local returnValues = {}
341
+ for index = 2, packed.n do
342
+ local serialized = ExecuteAction._serializeValue(packed[index], 1, {})
343
+ if serialized == nil then
344
+ serialized = { type = "Nil" }
345
+ end
346
+ returnValues[index - 1] = serialized
347
+ end
348
+ return returnValues
290
349
  end
291
350
 
292
- -- Reset the internal queue state (for testing).
293
- function ExecuteAction._resetQueue()
294
- _queue = {}
295
- _processing = false
351
+ --[[
352
+ Produces the SerializedReturnValue shapes declared in web-socket-protocol.ts.
353
+ Duplicates the marshaller in query-data-model.lua, since actions cannot
354
+ require each other.
355
+ ]]
356
+ function ExecuteAction._serializeValue(value: any, depth: number, seen: { [any]: boolean }): any
357
+ local valueType = typeof(value)
358
+
359
+ if valueType == "nil" or valueType == "string" or valueType == "boolean" then
360
+ return value
361
+ elseif valueType == "number" then
362
+ -- inf and nan have no JSON spelling, and encoding one produces a
363
+ -- document the server cannot decode, losing the entire message
364
+ if value ~= value or value == math.huge or value == -math.huge then
365
+ return ExecuteAction._unsupported("number", tostring(value))
366
+ end
367
+ return value
368
+ elseif valueType == "table" then
369
+ if depth > MAX_DEPTH then
370
+ return ExecuteAction._unsupported("table", "<max depth exceeded>")
371
+ end
372
+ if seen[value] then
373
+ return ExecuteAction._unsupported("table", "<cycle>")
374
+ end
375
+
376
+ seen[value] = true
377
+ local serialized = ExecuteAction._serializeTable(value, depth, seen)
378
+ seen[value] = nil
379
+ return serialized
380
+ elseif valueType == "Vector3" then
381
+ return { type = "Vector3", value = { value.X, value.Y, value.Z } }
382
+ elseif valueType == "Vector2" then
383
+ return { type = "Vector2", value = { value.X, value.Y } }
384
+ elseif valueType == "CFrame" then
385
+ return { type = "CFrame", value = { value:GetComponents() } }
386
+ elseif valueType == "Color3" then
387
+ return { type = "Color3", value = { value.R, value.G, value.B } }
388
+ elseif valueType == "UDim2" then
389
+ return { type = "UDim2", value = { value.X.Scale, value.X.Offset, value.Y.Scale, value.Y.Offset } }
390
+ elseif valueType == "UDim" then
391
+ return { type = "UDim", value = { value.Scale, value.Offset } }
392
+ elseif valueType == "BrickColor" then
393
+ return { type = "BrickColor", name = value.Name, value = value.Number }
394
+ elseif valueType == "EnumItem" then
395
+ return { type = "EnumItem", enum = tostring(value.EnumType), name = value.Name, value = value.Value }
396
+ elseif valueType == "Instance" then
397
+ return { type = "Instance", className = value.ClassName, path = value:GetFullName() }
398
+ else
399
+ return ExecuteAction._unsupported(valueType, tostring(value))
400
+ end
296
401
  end
297
402
 
298
- function ExecuteAction.teardown()
299
- for _, item in _queue do
300
- if item.sendMessage then
301
- item.sendMessage({
302
- type = "scriptComplete",
303
- sessionId = item.sessionId,
304
- requestId = item.requestId,
305
- payload = {
306
- success = false,
307
- error = "Action re-registered, request cancelled",
308
- code = "ACTION_REPLACED",
309
- },
310
- })
403
+ --[[
404
+ An array-like table becomes a JSON array, anything else a JSON object with
405
+ string keys. Mixed tables take the object branch on purpose: JSONEncode
406
+ rejects them, and a rejected encode drops the whole scriptComplete message.
407
+ ]]
408
+ function ExecuteAction._serializeTable(value: { [any]: any }, depth: number, seen: { [any]: boolean }): any
409
+ local count = 0
410
+ for _ in value do
411
+ count += 1
412
+ end
413
+
414
+ if count == #value then
415
+ local array = {}
416
+ for index = 1, count do
417
+ array[index] = ExecuteAction._serializeValue(value[index], depth + 1, seen)
311
418
  end
419
+ return array
312
420
  end
313
- _queue = {}
314
- _processing = false
421
+
422
+ local map: { [string]: any } = {}
423
+ for key, item in value do
424
+ map[if type(key) == "string" then key else tostring(key)] = ExecuteAction._serializeValue(item, depth + 1, seen)
425
+ end
426
+ return map
427
+ end
428
+
429
+ function ExecuteAction._unsupported(typeName: string, text: string): { [string]: any }
430
+ return { type = "Unsupported", typeName = typeName, toString = text }
315
431
  end
316
432
 
317
433
  return ExecuteAction