@quenty/studio-bridge 0.21.0 → 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 (491) hide show
  1. package/CHANGELOG.md +12 -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/linux/linux-studio-launcher.d.ts.map +1 -1
  277. package/dist/src/linux/linux-studio-launcher.js +10 -0
  278. package/dist/src/linux/linux-studio-launcher.js.map +1 -1
  279. package/dist/src/linux/linux-studio-launcher.test.d.ts +2 -0
  280. package/dist/src/linux/linux-studio-launcher.test.d.ts.map +1 -0
  281. package/dist/src/linux/linux-studio-launcher.test.js +68 -0
  282. package/dist/src/linux/linux-studio-launcher.test.js.map +1 -0
  283. package/dist/src/plugin/persistent-plugin-installer.d.ts.map +1 -1
  284. package/dist/src/plugin/persistent-plugin-installer.js +2 -1
  285. package/dist/src/plugin/persistent-plugin-installer.js.map +1 -1
  286. package/dist/src/plugin/persistent-plugin-installer.test.js +3 -0
  287. package/dist/src/plugin/persistent-plugin-installer.test.js.map +1 -1
  288. package/dist/src/plugin/plugin-build-version.d.ts +7 -0
  289. package/dist/src/plugin/plugin-build-version.d.ts.map +1 -0
  290. package/dist/src/plugin/plugin-build-version.js +42 -0
  291. package/dist/src/plugin/plugin-build-version.js.map +1 -0
  292. package/dist/src/plugin/plugin-build-version.test.d.ts +2 -0
  293. package/dist/src/plugin/plugin-build-version.test.d.ts.map +1 -0
  294. package/dist/src/plugin/plugin-build-version.test.js +41 -0
  295. package/dist/src/plugin/plugin-build-version.test.js.map +1 -0
  296. package/dist/src/plugin/plugin-injector.d.ts.map +1 -1
  297. package/dist/src/plugin/plugin-injector.js +7 -1
  298. package/dist/src/plugin/plugin-injector.js.map +1 -1
  299. package/dist/src/process/studio-process-closer.d.ts +35 -0
  300. package/dist/src/process/studio-process-closer.d.ts.map +1 -0
  301. package/dist/src/process/studio-process-closer.js +129 -0
  302. package/dist/src/process/studio-process-closer.js.map +1 -0
  303. package/dist/src/process/studio-process-closer.test.d.ts +2 -0
  304. package/dist/src/process/studio-process-closer.test.d.ts.map +1 -0
  305. package/dist/src/process/studio-process-closer.test.js +80 -0
  306. package/dist/src/process/studio-process-closer.test.js.map +1 -0
  307. package/dist/src/process/studio-settings.d.ts +14 -0
  308. package/dist/src/process/studio-settings.d.ts.map +1 -0
  309. package/dist/src/process/studio-settings.js +50 -0
  310. package/dist/src/process/studio-settings.js.map +1 -0
  311. package/dist/src/process/studio-settings.test.d.ts +2 -0
  312. package/dist/src/process/studio-settings.test.d.ts.map +1 -0
  313. package/dist/src/process/studio-settings.test.js +16 -0
  314. package/dist/src/process/studio-settings.test.js.map +1 -0
  315. package/dist/src/process/studio-window-state.d.ts +13 -0
  316. package/dist/src/process/studio-window-state.d.ts.map +1 -0
  317. package/dist/src/process/studio-window-state.js +48 -0
  318. package/dist/src/process/studio-window-state.js.map +1 -0
  319. package/dist/src/server/studio-bridge-server.d.ts +3 -1
  320. package/dist/src/server/studio-bridge-server.d.ts.map +1 -1
  321. package/dist/src/server/studio-bridge-server.js +12 -4
  322. package/dist/src/server/studio-bridge-server.js.map +1 -1
  323. package/dist/src/server/web-socket-protocol.d.ts +173 -6
  324. package/dist/src/server/web-socket-protocol.d.ts.map +1 -1
  325. package/dist/src/server/web-socket-protocol.js +244 -1
  326. package/dist/src/server/web-socket-protocol.js.map +1 -1
  327. package/dist/src/server/web-socket-protocol.test.js +48 -0
  328. package/dist/src/server/web-socket-protocol.test.js.map +1 -1
  329. package/dist/src/test/e2e/persistent-session.test.js +121 -0
  330. package/dist/src/test/e2e/persistent-session.test.js.map +1 -1
  331. package/dist/src/test/e2e/split-server.test.d.ts +3 -5
  332. package/dist/src/test/e2e/split-server.test.d.ts.map +1 -1
  333. package/dist/src/test/e2e/split-server.test.js +3 -5
  334. package/dist/src/test/e2e/split-server.test.js.map +1 -1
  335. package/dist/tsconfig.tsbuildinfo +1 -1
  336. package/docker/Dockerfile +3 -0
  337. package/package.json +8 -6
  338. package/src/bridge/bridge-connection.test.ts +126 -0
  339. package/src/bridge/bridge-connection.ts +217 -22
  340. package/src/bridge/bridge-session.test.ts +6 -4
  341. package/src/bridge/bridge-session.ts +273 -28
  342. package/src/bridge/index.ts +3 -0
  343. package/src/bridge/internal/bridge-client.test.ts +23 -0
  344. package/src/bridge/internal/bridge-client.ts +25 -7
  345. package/src/bridge/internal/bridge-host.test.ts +38 -0
  346. package/src/bridge/internal/bridge-host.ts +47 -0
  347. package/src/bridge/internal/hand-off.ts +4 -1
  348. package/src/bridge/internal/host-protocol.test.ts +15 -0
  349. package/src/bridge/internal/host-protocol.ts +148 -37
  350. package/src/bridge/internal/instance-state.ts +34 -0
  351. package/src/bridge/internal/transport-client.ts +2 -1
  352. package/src/bridge/internal/transport-server.ts +5 -1
  353. package/src/bridge/types.ts +47 -2
  354. package/src/cli/adapters/cli-command-adapter-handler.test.ts +28 -0
  355. package/src/cli/adapters/cli-command-adapter.ts +150 -18
  356. package/src/cli/adapters/group-builder.test.ts +34 -2
  357. package/src/cli/adapters/group-builder.ts +52 -20
  358. package/src/cli/adapters/help-formatter.test.ts +1 -1
  359. package/src/cli/adapters/help-formatter.ts +4 -3
  360. package/src/cli/adapters/playtest-edit-guard.test.ts +54 -0
  361. package/src/cli/args/global-args.ts +1 -1
  362. package/src/cli/cli-output-context.test.ts +55 -0
  363. package/src/cli/cli-output-context.ts +50 -0
  364. package/src/cli/cli.ts +49 -17
  365. package/src/cli/managed-studio-run.test.ts +71 -0
  366. package/src/cli/managed-studio-run.ts +135 -0
  367. package/src/cli/resolve-session.test.ts +1 -1
  368. package/src/cli/resolve-session.ts +1 -1
  369. package/src/cli/script-executor.ts +99 -51
  370. package/src/commands/console/exec/exec.test.ts +96 -3
  371. package/src/commands/console/exec/exec.ts +59 -6
  372. package/src/commands/console/exec/execute.lua +356 -240
  373. package/src/commands/console/exec/execute.spec.lua +286 -0
  374. package/src/commands/console/logs/logs.test.ts +76 -4
  375. package/src/commands/console/logs/logs.ts +132 -17
  376. package/src/commands/console/logs/query-logs.lua +85 -52
  377. package/src/commands/console/logs/query-logs.spec.lua +132 -0
  378. package/src/commands/explorer/export/export-instances.lua +108 -0
  379. package/src/commands/explorer/export/export-instances.spec.lua +87 -0
  380. package/src/commands/explorer/export/export.test.ts +27 -0
  381. package/src/commands/explorer/export/export.ts +60 -0
  382. package/src/commands/explorer/find/find-instances.lua +174 -0
  383. package/src/commands/explorer/find/find-instances.spec.lua +122 -0
  384. package/src/commands/explorer/find/find.test.ts +53 -0
  385. package/src/commands/explorer/find/find.ts +113 -0
  386. package/src/commands/explorer/grep/grep-scripts.lua +214 -0
  387. package/src/commands/explorer/grep/grep-scripts.spec.lua +130 -0
  388. package/src/commands/explorer/grep/grep.test.ts +45 -0
  389. package/src/commands/explorer/grep/grep.ts +93 -0
  390. package/src/commands/explorer/import/import-instances.lua +106 -0
  391. package/src/commands/explorer/import/import-instances.spec.lua +84 -0
  392. package/src/commands/explorer/import/import.test.ts +60 -0
  393. package/src/commands/explorer/import/import.ts +66 -0
  394. package/src/commands/explorer/query/query-data-model.lua +124 -152
  395. package/src/commands/explorer/query/query-data-model.spec.lua +150 -0
  396. package/src/commands/formatters.test.ts +7 -1
  397. package/src/commands/framework/action-loader.test.ts +28 -0
  398. package/src/commands/framework/action-loader.ts +10 -2
  399. package/src/commands/framework/arg-builder.ts +26 -3
  400. package/src/commands/framework/define-command.ts +32 -3
  401. package/src/commands/framework/index.ts +1 -0
  402. package/src/commands/index.ts +50 -0
  403. package/src/commands/linux/inject-credentials/inject-credentials.ts +1 -3
  404. package/src/commands/linux/setup/setup.ts +1 -3
  405. package/src/commands/linux/status/status.ts +105 -98
  406. package/src/commands/playtest/player/add/add.ts +85 -0
  407. package/src/commands/playtest/player/list/list.ts +57 -0
  408. package/src/commands/playtest/player/playtest-players.test.ts +146 -0
  409. package/src/commands/playtest/player/playtest-players.ts +270 -0
  410. package/src/commands/playtest/player/remove/remove.ts +78 -0
  411. package/src/commands/playtest/playtest-sessions.test.ts +143 -0
  412. package/src/commands/playtest/playtest-sessions.ts +417 -0
  413. package/src/commands/playtest/restart/restart.ts +199 -0
  414. package/src/commands/playtest/run/playtest-runner.luau +264 -0
  415. package/src/commands/playtest/run/playtest-runner.spec.lua +71 -0
  416. package/src/commands/playtest/run/run-playtest-script.lua +139 -0
  417. package/src/commands/playtest/run/run-playtest-script.spec.lua +43 -0
  418. package/src/commands/playtest/run/run.ts +239 -0
  419. package/src/commands/playtest/start/start-playtest.lua +108 -0
  420. package/src/commands/playtest/start/start.ts +138 -0
  421. package/src/commands/playtest/stop/end-playtest.lua +47 -0
  422. package/src/commands/playtest/stop/stop.ts +51 -0
  423. package/src/commands/process/close/close.test.ts +250 -0
  424. package/src/commands/process/close/close.ts +183 -17
  425. package/src/commands/process/info/info.test.ts +198 -44
  426. package/src/commands/process/info/info.ts +204 -15
  427. package/src/commands/process/info/query-state.lua +42 -29
  428. package/src/commands/process/info/query-state.spec.lua +39 -0
  429. package/src/commands/process/launch/launch.test.ts +138 -21
  430. package/src/commands/process/launch/launch.ts +69 -9
  431. package/src/commands/process/list/list.test.ts +1 -0
  432. package/src/commands/process/list/list.ts +14 -6
  433. package/src/commands/process/run/run.ts +26 -5
  434. package/src/commands/serve/serve.test.ts +17 -2
  435. package/src/commands/serve/serve.ts +25 -6
  436. package/src/commands/viewport/screenshot/capture-screenshot.lua +175 -182
  437. package/src/commands/viewport/screenshot/capture-screenshot.spec.lua +129 -0
  438. package/src/commands/viewport/screenshot/screenshot.test.ts +7 -10
  439. package/src/commands/viewport/screenshot/screenshot.ts +6 -1
  440. package/src/deploy/deploy-places.test.ts +127 -0
  441. package/src/deploy/deploy-places.ts +229 -0
  442. package/src/index.ts +11 -0
  443. package/src/linux/linux-studio-launcher.test.ts +89 -0
  444. package/src/linux/linux-studio-launcher.ts +11 -0
  445. package/src/plugin/persistent-plugin-installer.test.ts +4 -0
  446. package/src/plugin/persistent-plugin-installer.ts +2 -1
  447. package/src/plugin/plugin-build-version.test.ts +55 -0
  448. package/src/plugin/plugin-build-version.ts +53 -0
  449. package/src/plugin/plugin-injector.ts +7 -1
  450. package/src/process/studio-process-closer.test.ts +107 -0
  451. package/src/process/studio-process-closer.ts +187 -0
  452. package/src/process/studio-settings.test.ts +18 -0
  453. package/src/process/studio-settings.ts +61 -0
  454. package/src/process/studio-window-state.ts +65 -0
  455. package/src/server/studio-bridge-server.ts +15 -4
  456. package/src/server/web-socket-protocol.test.ts +55 -0
  457. package/src/server/web-socket-protocol.ts +468 -8
  458. package/src/test/e2e/persistent-session.test.ts +137 -0
  459. package/src/test/e2e/split-server.test.ts +3 -5
  460. package/templates/studio-bridge-plugin/default.project.json +3 -0
  461. package/templates/studio-bridge-plugin/src/Shared/ActionRouter.lua +208 -159
  462. package/templates/studio-bridge-plugin/src/Shared/ActionRouter.spec.lua +418 -0
  463. package/templates/studio-bridge-plugin/src/Shared/BuiltInActions.lua +94 -0
  464. package/templates/studio-bridge-plugin/src/Shared/BuiltInActions.spec.lua +166 -0
  465. package/templates/studio-bridge-plugin/src/Shared/DiscoveryStateMachine.lua +118 -137
  466. package/templates/studio-bridge-plugin/src/Shared/DiscoveryStateMachine.spec.lua +228 -0
  467. package/templates/studio-bridge-plugin/src/Shared/MessageBuffer.lua +80 -109
  468. package/templates/studio-bridge-plugin/src/Shared/MessageBuffer.spec.lua +116 -0
  469. package/templates/studio-bridge-plugin/src/Shared/StudioBridgeConnection.lua +233 -0
  470. package/templates/studio-bridge-plugin/src/Shared/StudioBridgeConnection.spec.lua +239 -0
  471. package/templates/studio-bridge-plugin/src/Shared/StudioBridgeLogUtils.lua +53 -0
  472. package/templates/studio-bridge-plugin/src/Shared/StudioBridgeLogUtils.spec.lua +27 -0
  473. package/templates/studio-bridge-plugin/src/Shared/StudioBridgeSessionUtils.lua +164 -0
  474. package/templates/studio-bridge-plugin/src/Shared/StudioBridgeSessionUtils.spec.lua +159 -0
  475. package/templates/studio-bridge-plugin/src/StudioBridgePlugin.server.lua +266 -368
  476. package/templates/studio-bridge-plugin/src/Vendor/png.lua +8 -2
  477. package/test/default.project.json +41 -0
  478. package/test/jest.config.lua +3 -0
  479. package/test/scripts/Server/ServerMain.server.lua +10 -0
  480. package/tsconfig.json +1 -0
  481. package/templates/studio-bridge-plugin/src/Shared/Protocol.lua +0 -132
  482. package/templates/studio-bridge-plugin-test/test/actions.test.lua +0 -580
  483. package/templates/studio-bridge-plugin-test/test/discovery.test.lua +0 -622
  484. package/templates/studio-bridge-plugin-test/test/execute-handler.test.lua +0 -517
  485. package/templates/studio-bridge-plugin-test/test/hash-lifecycle.test.lua +0 -814
  486. package/templates/studio-bridge-plugin-test/test/integration.test.lua +0 -1020
  487. package/templates/studio-bridge-plugin-test/test/new-actions.test.lua +0 -339
  488. package/templates/studio-bridge-plugin-test/test/plugin-entry.test.lua +0 -493
  489. package/templates/studio-bridge-plugin-test/test/protocol.test.lua +0 -506
  490. package/templates/studio-bridge-plugin-test/test/roblox-mocks.lua +0 -108
  491. package/templates/studio-bridge-plugin-test/test/test-runner.lua +0 -112
@@ -1,205 +1,163 @@
1
1
  --!optimize 2
2
- --[[
3
- CaptureScreenshot action handler for the studio-bridge plugin.
2
+ --[=[
3
+ Action that captures the Studio viewport and answers with it as base64.
4
4
 
5
- Captures the Studio viewport using CaptureService, loads the result into
6
- an EditableImage to read raw RGBA pixels, encodes as PNG via png-luau,
7
- base64-encodes the PNG via EncodingService, and sends it over the WebSocket.
5
+ Captures with CaptureService, reads the RGBA pixels through an EditableImage,
6
+ encodes them as PNG when the plugin supplied an encoder (raw RGBA otherwise),
7
+ and base64-encodes the result with EncodingService.
8
8
 
9
- Protocol:
10
- Request: { type: "captureScreenshot", payload: { format?: "png" } }
11
- Response: { type: "screenshotResult", payload: { data, format, width, height } }
12
- ]]
9
+ Request: `{ type: "captureScreenshot", payload: { format?: "png" } }`
10
+ Response: `{ type: "screenshotResult", payload: { data, format, width, height } }`
11
+
12
+ @class CaptureScreenshotAction
13
+ ]=]
13
14
 
14
- -- PNG encoder is injected via router._vendorPng (set by the plugin before
15
- -- actions are pushed). Falls back gracefully to raw RGBA if unavailable.
16
- local _png = nil
17
- local _disposed = false
15
+ -- ReadPixelsBuffer reads at most 1024x1024
16
+ local MAX_READ_SIZE = 1024
18
17
 
19
18
  local CaptureScreenshotAction = {}
20
19
 
21
- -- ---------------------------------------------------------------------------
22
- -- Helpers
23
- -- ---------------------------------------------------------------------------
20
+ type SendMessage = (message: { [string]: any }) -> ()
24
21
 
25
- local function buildResponse(sessionId: string, requestId: string?, payload: { [string]: any }): { [string]: any }
26
- local msg: { [string]: any } = {
27
- type = "screenshotResult",
28
- sessionId = sessionId,
29
- payload = payload,
30
- }
31
- if requestId ~= nil and requestId ~= "" then
32
- msg.requestId = requestId
33
- end
34
- return msg
22
+ export type Services = {
23
+ captureService: any,
24
+ assetService: any,
25
+ encodingService: any,
26
+ }
27
+
28
+ -- The plugin hands its PNG encoder over through router._vendorPng
29
+ local png: any = nil
30
+ local isTornDown = false
31
+
32
+ --[=[
33
+ Registers the `captureScreenshot` handler. Capturing replies asynchronously,
34
+ so it needs `sendMessage`.
35
+ ]=]
36
+ function CaptureScreenshotAction.register(router: any, sendMessage: SendMessage?)
37
+ isTornDown = false
38
+ png = router._vendorPng
39
+
40
+ router:setResponseType("captureScreenshot", "screenshotResult")
41
+ router:register(
42
+ "captureScreenshot",
43
+ function(_payload: { [string]: any }, requestId: string, sessionId: string): { [string]: any }?
44
+ if not sendMessage then
45
+ return {
46
+ code = "CAPABILITY_NOT_SUPPORTED",
47
+ message = "Screenshot capture requires a sendMessage callback",
48
+ }
49
+ end
50
+
51
+ -- Lune has no task library, so fall back to running inline there
52
+ local spawnOk = pcall(function()
53
+ task.spawn(CaptureScreenshotAction._captureAsync, sendMessage, requestId, sessionId)
54
+ end)
55
+ if not spawnOk then
56
+ CaptureScreenshotAction._captureAsync(sendMessage, requestId, sessionId)
57
+ end
58
+
59
+ -- Replied through sendMessage, so the router must not reply as well
60
+ return nil
61
+ end
62
+ )
35
63
  end
36
64
 
37
- local function buildError(sessionId: string, requestId: string?, code: string, message: string): { [string]: any }
38
- local msg: { [string]: any } = {
39
- type = "error",
40
- sessionId = sessionId,
41
- payload = { code = code, message = message },
42
- }
43
- if requestId ~= nil and requestId ~= "" then
44
- msg.requestId = requestId
45
- end
46
- return msg
65
+ --[=[
66
+ Stops an in-flight capture from replying.
67
+ ]=]
68
+ function CaptureScreenshotAction.teardown()
69
+ isTornDown = true
47
70
  end
48
71
 
49
- -- ---------------------------------------------------------------------------
50
- -- Core capture logic (runs in a spawned thread)
51
- -- ---------------------------------------------------------------------------
72
+ function CaptureScreenshotAction._captureAsync(
73
+ sendMessage: SendMessage,
74
+ requestId: string?,
75
+ sessionId: string,
76
+ services: Services?
77
+ )
78
+ local function fail(code: string, message: string)
79
+ sendMessage(CaptureScreenshotAction._buildMessage("error", sessionId, requestId, {
80
+ code = code,
81
+ message = message,
82
+ }))
83
+ end
52
84
 
53
- local function captureAsync(sendMessage: (msg: { [string]: any }) -> (), requestId: string?, sessionId: string)
54
- -- Resolve services (deferred so the module loads in Lune test context)
55
- local getServiceOk, captureServiceOrErr, assetServiceOrErr, encodingServiceOrErr = pcall(function()
56
- return game:GetService("CaptureService"), game:GetService("AssetService"), game:GetService("EncodingService")
85
+ -- Resolved here rather than at load, so the module still loads under Lune
86
+ local servicesOk, servicesOrErr = pcall(function()
87
+ return services
88
+ or {
89
+ captureService = game:GetService("CaptureService"),
90
+ assetService = game:GetService("AssetService"),
91
+ encodingService = game:GetService("EncodingService"),
92
+ }
57
93
  end)
58
- if not getServiceOk then
59
- sendMessage(
60
- buildError(sessionId, requestId, "CAPTURE_FAILED", `Required services not available: {captureServiceOrErr}`)
61
- )
94
+ if not servicesOk then
95
+ fail("CAPTURE_FAILED", `Required services not available: {servicesOrErr}`)
62
96
  return
63
97
  end
64
- local CaptureService = captureServiceOrErr
65
- local AssetService = assetServiceOrErr
66
- local EncodingService = encodingServiceOrErr
67
-
68
- -- Step 1: Capture the viewport via CaptureService
69
- local callerThread = coroutine.running()
70
- local contentId: string? = nil
71
- local captureOk, captureErr = pcall(function()
72
- CaptureService:CaptureScreenshot(function(id)
73
- contentId = id
74
- task.spawn(callerThread)
75
- end)
76
- end)
98
+ local assetService = servicesOrErr.assetService
99
+ local encodingService = servicesOrErr.encodingService
77
100
 
101
+ local captureOk, contentIdOrErr = CaptureScreenshotAction._captureContentIdAsync(servicesOrErr.captureService)
102
+ if isTornDown then
103
+ return
104
+ end
78
105
  if not captureOk then
79
- sendMessage(buildError(sessionId, requestId, "CAPTURE_FAILED", `CaptureService failed: {captureErr}`))
106
+ fail("CAPTURE_FAILED", `CaptureService failed: {contentIdOrErr}`)
80
107
  return
81
108
  end
82
109
 
83
- -- Wait for the callback
84
- coroutine.yield()
85
-
86
- if _disposed then return end
87
-
88
- if not contentId or contentId == "" then
89
- sendMessage(buildError(sessionId, requestId, "CAPTURE_FAILED", "CaptureService returned no content ID"))
110
+ local contentId = contentIdOrErr
111
+ if contentId == nil or contentId == "" then
112
+ fail("CAPTURE_FAILED", "CaptureService returned no content ID")
90
113
  return
91
114
  end
92
115
 
93
- -- Step 2: Load into an EditableImage to read pixels
94
116
  local editableOk, editableImage = pcall(function()
95
- return AssetService:CreateEditableImageAsync(Content.fromUri(contentId :: string))
117
+ return assetService:CreateEditableImageAsync(Content.fromUri(contentId))
96
118
  end)
97
-
98
- if not editableOk or not editableImage then
99
- sendMessage(
100
- buildError(
101
- sessionId,
102
- requestId,
103
- "EDITABLE_IMAGE_FAILED",
104
- `Failed to create EditableImage from capture: {editableImage or "unknown error"}`
105
- )
106
- )
119
+ if not (editableOk and editableImage) then
120
+ fail("EDITABLE_IMAGE_FAILED", `Failed to create EditableImage from capture: {editableImage or "unknown error"}`)
107
121
  return
108
122
  end
109
123
 
110
- -- Step 3: Read all pixels as RGBA buffer
111
- -- ReadPixelsBuffer has a 1024x1024 limit. If the screenshot is larger,
112
- -- scale it down into a new EditableImage that fits within the limit.
113
- local MAX_SIZE = 1024
114
- local srcWidth = editableImage.Size.X
115
- local srcHeight = editableImage.Size.Y
116
- local width = srcWidth
117
- local height = srcHeight
118
-
119
- local readTarget = editableImage
120
- local scaledImage = nil
121
-
122
- if srcWidth > MAX_SIZE or srcHeight > MAX_SIZE then
123
- local scale = math.min(MAX_SIZE / srcWidth, MAX_SIZE / srcHeight)
124
- width = math.floor(srcWidth * scale)
125
- height = math.floor(srcHeight * scale)
126
-
127
- local scaleOk, scaleResult = pcall(function()
128
- local scaled = AssetService:CreateEditableImage({ Size = Vector2.new(width, height) })
129
- scaled:DrawImageTransformed(
130
- Vector2.new(width / 2, height / 2), -- center position
131
- Vector2.new(scale, scale), -- scale
132
- 0, -- rotation
133
- editableImage
134
- )
135
- return scaled
136
- end)
137
-
138
- if scaleOk and scaleResult then
139
- scaledImage = scaleResult
140
- readTarget = scaleResult
141
- else
142
- -- If scaling fails, just read the top-left 1024x1024 corner
143
- width = math.min(srcWidth, MAX_SIZE)
144
- height = math.min(srcHeight, MAX_SIZE)
145
- end
146
- end
124
+ local readTarget, scaledImage, width, height = CaptureScreenshotAction._fitToReadLimit(assetService, editableImage)
147
125
 
148
- local pixelsOk, pixelsBuf = pcall(function()
126
+ local pixelsOk, pixels = pcall(function()
149
127
  return readTarget:ReadPixelsBuffer(Vector2.zero, Vector2.new(width, height))
150
128
  end)
151
129
 
152
- -- Clean up EditableImages immediately to free memory
153
- pcall(function()
154
- editableImage:Destroy()
155
- end)
130
+ -- Free the images right away, they hold the whole capture in memory
131
+ pcall(editableImage.Destroy, editableImage)
156
132
  if scaledImage then
157
- pcall(function()
158
- scaledImage:Destroy()
159
- end)
133
+ pcall(scaledImage.Destroy, scaledImage)
160
134
  end
161
135
 
162
- if not pixelsOk or not pixelsBuf then
163
- sendMessage(
164
- buildError(
165
- sessionId,
166
- requestId,
167
- "PIXEL_READ_FAILED",
168
- `Failed to read pixels: {pixelsBuf or "unknown error"}`
169
- )
170
- )
136
+ if not (pixelsOk and pixels) then
137
+ fail("PIXEL_READ_FAILED", `Failed to read pixels: {pixels or "unknown error"}`)
171
138
  return
172
139
  end
173
140
 
174
- -- Step 4: Encode as PNG (if available) or raw RGBA, then base64
175
- local dataBuf = pixelsBuf
141
+ local data = pixels
176
142
  local format = "rgba"
177
-
178
- if _png then
179
- local pngOk, pngBuf = pcall(_png.encode, pixelsBuf, { width = width, height = height })
180
- if pngOk and pngBuf then
181
- dataBuf = pngBuf
143
+ if png then
144
+ local pngOk, pngData = pcall(png.encode, pixels, { width = width, height = height })
145
+ if pngOk and pngData then
146
+ data = pngData
182
147
  format = "png"
183
148
  end
184
149
  end
185
150
 
186
151
  local encodeOk, encoded = pcall(function()
187
- return EncodingService:Base64Encode(dataBuf)
152
+ -- JSONEncode turns a buffer into a { t = "buffer", zbase64 } object, so send a string
153
+ return buffer.tostring(encodingService:Base64Encode(data))
188
154
  end)
189
- if not encodeOk or not encoded then
190
- sendMessage(
191
- buildError(
192
- sessionId,
193
- requestId,
194
- "ENCODE_FAILED",
195
- `Failed to base64 encode data: {encoded or "unknown error"}`
196
- )
197
- )
155
+ if not (encodeOk and encoded) then
156
+ fail("ENCODE_FAILED", `Failed to base64 encode data: {encoded or "unknown error"}`)
198
157
  return
199
158
  end
200
159
 
201
- -- Step 5: Send the result
202
- sendMessage(buildResponse(sessionId, requestId, {
160
+ sendMessage(CaptureScreenshotAction._buildMessage("screenshotResult", sessionId, requestId, {
203
161
  data = encoded,
204
162
  format = format,
205
163
  width = width,
@@ -207,41 +165,76 @@ local function captureAsync(sendMessage: (msg: { [string]: any }) -> (), request
207
165
  }))
208
166
  end
209
167
 
210
- -- ---------------------------------------------------------------------------
211
- -- Public API
212
- -- ---------------------------------------------------------------------------
213
-
214
- function CaptureScreenshotAction.register(router: any, sendMessage: ((msg: { [string]: any }) -> ())?)
215
- _disposed = false
216
- router:setResponseType("captureScreenshot", "screenshotResult")
217
- -- Capture the PNG encoder from the router's shared deps (set by the plugin)
218
- _png = router._vendorPng or nil
219
-
220
- router:register("captureScreenshot", function(_payload: { [string]: any }, requestId: string, sessionId: string): { [string]: any }?
221
- if not sendMessage then
222
- return {
223
- code = "CAPABILITY_NOT_SUPPORTED",
224
- message = "Screenshot capture requires a sendMessage callback",
225
- }
226
- end
168
+ --[[
169
+ Returns true and the capture's content id, or false and the error
170
+ CaptureService threw. Yields until the capture callback fires.
171
+ ]]
172
+ function CaptureScreenshotAction._captureContentIdAsync(captureService: any): (boolean, any)
173
+ local callerThread = coroutine.running()
174
+ local isWaiting = false
175
+ local isReady = false
176
+ local contentId: string? = nil
227
177
 
228
- -- Run asynchronously since CaptureService uses a callback.
229
- -- pcall guards against Lune test context where task is not available.
230
- local spawnOk = pcall(function()
231
- task.spawn(captureAsync, sendMessage, requestId, sessionId)
178
+ local captureOk, captureErr = pcall(function()
179
+ captureService:CaptureScreenshot(function(id)
180
+ contentId = id
181
+ isReady = true
182
+ if isWaiting then
183
+ task.spawn(callerThread)
184
+ end
232
185
  end)
233
- if not spawnOk then
234
- -- Fallback: call directly (will fail on GetService in Lune)
235
- captureAsync(sendMessage, requestId, sessionId)
236
- end
186
+ end)
187
+ if not captureOk then
188
+ return false, captureErr
189
+ end
190
+
191
+ if not isReady then
192
+ isWaiting = true
193
+ coroutine.yield()
194
+ end
195
+
196
+ return true, contentId
197
+ end
198
+
199
+ --[[
200
+ Returns an image no larger than the read limit, scaled down from the capture
201
+ when needed. If scaling fails, falls back to the capture's top-left corner.
202
+ ]]
203
+ function CaptureScreenshotAction._fitToReadLimit(assetService: any, editableImage: any): (any, any?, number, number)
204
+ local sourceWidth = editableImage.Size.X
205
+ local sourceHeight = editableImage.Size.Y
206
+ if sourceWidth <= MAX_READ_SIZE and sourceHeight <= MAX_READ_SIZE then
207
+ return editableImage, nil, sourceWidth, sourceHeight
208
+ end
209
+
210
+ local scale = math.min(MAX_READ_SIZE / sourceWidth, MAX_READ_SIZE / sourceHeight)
211
+ local width = math.floor(sourceWidth * scale)
212
+ local height = math.floor(sourceHeight * scale)
237
213
 
238
- -- Return nil so ActionRouter does not generate a wrapped response
239
- return nil
214
+ local scaleOk, scaledImage = pcall(function()
215
+ local scaled = assetService:CreateEditableImage({ Size = Vector2.new(width, height) })
216
+ scaled:DrawImageTransformed(Vector2.new(width / 2, height / 2), Vector2.new(scale, scale), 0, editableImage)
217
+ return scaled
240
218
  end)
219
+ if scaleOk and scaledImage then
220
+ return scaledImage, scaledImage, width, height
221
+ end
222
+
223
+ return editableImage, nil, math.min(sourceWidth, MAX_READ_SIZE), math.min(sourceHeight, MAX_READ_SIZE)
241
224
  end
242
225
 
243
- function CaptureScreenshotAction.teardown()
244
- _disposed = true
226
+ function CaptureScreenshotAction._buildMessage(
227
+ messageType: string,
228
+ sessionId: string,
229
+ requestId: string?,
230
+ payload: { [string]: any }
231
+ ): { [string]: any }
232
+ return {
233
+ type = messageType,
234
+ sessionId = sessionId,
235
+ requestId = if requestId ~= "" then requestId else nil,
236
+ payload = payload,
237
+ }
245
238
  end
246
239
 
247
240
  return CaptureScreenshotAction
@@ -0,0 +1,129 @@
1
+ --!strict
2
+ --[[
3
+ @class capture-screenshot.spec.lua
4
+ ]]
5
+
6
+ local require = require(script.Parent.loader).load(script)
7
+
8
+ local EncodingService = game:GetService("EncodingService")
9
+ local HttpService = game:GetService("HttpService")
10
+
11
+ local ActionRouter = require("ActionRouter")
12
+ local CaptureScreenshotAction = require("capture-screenshot")
13
+ local Jest = require("Jest")
14
+
15
+ local describe = Jest.Globals.describe
16
+ local expect = Jest.Globals.expect
17
+ local it = Jest.Globals.it
18
+
19
+ local function setup()
20
+ CaptureScreenshotAction.register(ActionRouter.new(), function() end)
21
+
22
+ local pixels = buffer.fromstring("\255\0\0\255\0\0\255\255")
23
+ local captureError: string? = nil
24
+ local callsBackImmediately = false
25
+
26
+ local services: CaptureScreenshotAction.Services = {
27
+ captureService = {
28
+ CaptureScreenshot = function(_self, onCaptureReady: (string) -> ())
29
+ if captureError then
30
+ error(captureError)
31
+ end
32
+ if callsBackImmediately then
33
+ onCaptureReady("rbxassetid://1")
34
+ else
35
+ task.defer(onCaptureReady, "rbxassetid://1")
36
+ end
37
+ end,
38
+ },
39
+ assetService = {
40
+ CreateEditableImageAsync = function()
41
+ return {
42
+ Size = Vector2.new(2, 1),
43
+ ReadPixelsBuffer = function()
44
+ return pixels
45
+ end,
46
+ Destroy = function() end,
47
+ }
48
+ end,
49
+ },
50
+ encodingService = EncodingService,
51
+ }
52
+
53
+ return {
54
+ pixels = pixels,
55
+ setCaptureError = function(message: string)
56
+ captureError = message
57
+ end,
58
+ setCallsBackImmediately = function()
59
+ callsBackImmediately = true
60
+ end,
61
+ captureAsync = function(): { [string]: any }
62
+ local sent: { [string]: any }? = nil
63
+ task.spawn(CaptureScreenshotAction._captureAsync, function(message)
64
+ sent = message
65
+ end, "request-1", "session-1", services)
66
+
67
+ local deadline = os.clock() + 5
68
+ while sent == nil and os.clock() < deadline do
69
+ task.wait()
70
+ end
71
+ assert(sent, "captureScreenshot never replied")
72
+
73
+ return HttpService:JSONDecode(HttpService:JSONEncode(sent))
74
+ end,
75
+ }
76
+ end
77
+
78
+ describe("captureScreenshot", function()
79
+ it("should answer CAPABILITY_NOT_SUPPORTED without sendMessage", function()
80
+ local router = ActionRouter.new()
81
+ CaptureScreenshotAction.register(router, nil)
82
+
83
+ local response = router:Dispatch({ type = "captureScreenshot", sessionId = "session-1", payload = {} })
84
+
85
+ expect(response).toEqual({
86
+ type = "screenshotResult",
87
+ sessionId = "session-1",
88
+ payload = {
89
+ code = "CAPABILITY_NOT_SUPPORTED",
90
+ message = "Screenshot capture requires a sendMessage callback",
91
+ },
92
+ })
93
+ end)
94
+
95
+ it("should send the pixels as a base64 string through JSON", function()
96
+ local controller = setup()
97
+
98
+ local message = controller.captureAsync()
99
+
100
+ expect(message.type).toBe("screenshotResult")
101
+ expect(message.requestId).toBe("request-1")
102
+ expect(message.payload.format).toBe("rgba")
103
+ expect(message.payload.width).toBe(2)
104
+ expect(message.payload.height).toBe(1)
105
+ expect(typeof(message.payload.data)).toBe("string")
106
+
107
+ local decoded = EncodingService:Base64Decode(buffer.fromstring(message.payload.data))
108
+ expect(buffer.tostring(decoded)).toBe(buffer.tostring(controller.pixels))
109
+ end)
110
+
111
+ it("should finish when CaptureService calls back before returning", function()
112
+ local controller = setup()
113
+ controller.setCallsBackImmediately()
114
+
115
+ local message = controller.captureAsync()
116
+
117
+ expect(message.type).toBe("screenshotResult")
118
+ end)
119
+
120
+ it("should answer CAPTURE_FAILED when CaptureService throws", function()
121
+ local controller = setup()
122
+ controller.setCaptureError("no capture permission")
123
+
124
+ local message = controller.captureAsync()
125
+
126
+ expect(message.type).toBe("error")
127
+ expect(message.payload.code).toBe("CAPTURE_FAILED")
128
+ end)
129
+ end)
@@ -145,22 +145,19 @@ describe('captureScreenshotHandlerAsync', () => {
145
145
  );
146
146
  });
147
147
 
148
- it('handles missing fields gracefully', async () => {
148
+ it('rejects data that is not a base64 string', async () => {
149
149
  const session = {
150
150
  captureScreenshotAsync: vi.fn().mockResolvedValue({
151
- data: undefined,
151
+ data: { m: null, t: 'buffer', zbase64: 'KLUv' },
152
152
  format: 'png',
153
- width: undefined,
154
- height: undefined,
153
+ width: 1,
154
+ height: 1,
155
155
  }),
156
156
  } as any;
157
157
 
158
- const result = await captureScreenshotHandlerAsync(session);
159
-
160
- expect(result.data).toBe('');
161
- expect(result.width).toBe(0);
162
- expect(result.height).toBe(0);
163
- expect(result.summary).toBe('Screenshot captured (0x0)');
158
+ await expect(captureScreenshotHandlerAsync(session)).rejects.toThrow(
159
+ 'The plugin sent screenshot data as object, not a base64 string'
160
+ );
164
161
  });
165
162
 
166
163
  it('passes options without affecting capture', async () => {
@@ -25,6 +25,11 @@ export async function captureScreenshotHandlerAsync(
25
25
  _options: ScreenshotOptions = {}
26
26
  ): Promise<ScreenshotResult> {
27
27
  const result: BridgeScreenshotResult = await session.captureScreenshotAsync();
28
+ if (typeof result.data !== 'string') {
29
+ throw new Error(
30
+ `The plugin sent screenshot data as ${typeof result.data}, not a base64 string`
31
+ );
32
+ }
28
33
 
29
34
  let pngBase64: string;
30
35
  if (result.format === 'rgba') {
@@ -32,7 +37,7 @@ export async function captureScreenshotHandlerAsync(
32
37
  const pngBuffer = rgbaToPng(rgbaBuffer, result.width, result.height);
33
38
  pngBase64 = pngBuffer.toString('base64');
34
39
  } else {
35
- pngBase64 = result.data ?? '';
40
+ pngBase64 = result.data;
36
41
  }
37
42
 
38
43
  return {