@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,96 +1,83 @@
1
- --[[
2
- Fixed-capacity ring buffer for storing log entries.
1
+ --!strict
2
+ --[=[
3
+ Fixed-capacity ring buffer of log entries. Once full, each push overwrites
4
+ the oldest entry.
3
5
 
4
- Entries are added via push(). When the buffer reaches capacity, new entries
5
- overwrite the oldest. Retrieval via get() supports both "head" (oldest first)
6
- and "tail" (newest first) directions with an optional count limit.
6
+ The query-logs action pushed over the wire calls `get`, which keeps its
7
+ lowercase name because a persistent plugin installed from an older release
8
+ still runs action source pushed by a newer CLI.
7
9
 
8
- No Roblox APIs. Pure logic, testable under Lune.
9
- ]]
10
+ @class MessageBuffer
11
+ ]=]
12
+
13
+ local DEFAULT_CAPACITY = 1000
10
14
 
11
15
  local MessageBuffer = {}
16
+ MessageBuffer.ClassName = "MessageBuffer"
12
17
  MessageBuffer.__index = MessageBuffer
13
18
 
14
- -- ---------------------------------------------------------------------------
15
- -- Constructor
16
- -- ---------------------------------------------------------------------------
17
-
18
- --[[
19
- Create a new MessageBuffer with the given capacity.
20
-
21
- @param capacity Maximum number of entries (default 1000).
22
- ]]
23
- function MessageBuffer.new(capacity: number?)
24
- local cap = capacity or 1000
25
- local self = setmetatable({
26
- _capacity = cap,
27
- _buffer = {} :: { { level: string, body: string, timestamp: number } },
28
- _head = 1, -- next write position (1-indexed)
29
- _count = 0, -- number of entries currently stored
30
- }, MessageBuffer)
19
+ export type LogEntry = {
20
+ level: string,
21
+ body: string,
22
+ timestamp: number,
23
+ }
24
+
25
+ export type Direction = "head" | "tail"
26
+
27
+ export type BufferSnapshot = {
28
+ entries: { LogEntry },
29
+ total: number,
30
+ bufferCapacity: number,
31
+ }
32
+
33
+ export type MessageBuffer = typeof(setmetatable(
34
+ {} :: {
35
+ _capacity: number,
36
+ _buffer: { LogEntry },
37
+ _nextIndex: number,
38
+ _count: number,
39
+ },
40
+ {} :: typeof({ __index = MessageBuffer })
41
+ ))
42
+
43
+ --[=[
44
+ Constructs a buffer holding at most `capacity` entries (default 1000).
45
+ ]=]
46
+ function MessageBuffer.new(capacity: number?): MessageBuffer
47
+ local self: MessageBuffer = setmetatable({} :: any, MessageBuffer)
48
+
49
+ self._capacity = capacity or DEFAULT_CAPACITY
50
+ self._buffer = {}
51
+ self._nextIndex = 1
52
+ self._count = 0
53
+
31
54
  return self
32
55
  end
33
56
 
34
- -- ---------------------------------------------------------------------------
35
- -- Push an entry into the buffer
36
- -- ---------------------------------------------------------------------------
37
-
38
- --[[
39
- Add an entry to the buffer. Overwrites the oldest entry if at capacity.
40
-
41
- @param entry Table with level, body, timestamp fields.
42
- ]]
43
- function MessageBuffer.push(
44
- self: any,
45
- entry: {
46
- level: string,
47
- body: string,
48
- timestamp: number,
49
- }
50
- )
51
- self._buffer[self._head] = entry
52
- self._head = (self._head % self._capacity) + 1
57
+ --[=[
58
+ Adds an entry, overwriting the oldest one when the buffer is full.
59
+ ]=]
60
+ function MessageBuffer.Push(self: MessageBuffer, entry: LogEntry)
61
+ self._buffer[self._nextIndex] = entry
62
+ self._nextIndex = (self._nextIndex % self._capacity) + 1
53
63
  if self._count < self._capacity then
54
- self._count = self._count + 1
64
+ self._count += 1
55
65
  end
56
66
  end
57
67
 
58
- -- ---------------------------------------------------------------------------
59
- -- Get entries from the buffer
60
- -- ---------------------------------------------------------------------------
61
-
62
- --[[
63
- Retrieve entries from the buffer.
64
-
65
- @param direction "head" for oldest first, "tail" for newest first (default "tail").
66
- @param count Maximum number of entries to return (default: all).
67
- @return Table with entries, total, and bufferCapacity.
68
- ]]
69
- function MessageBuffer.get(
70
- self: any,
71
- direction: string?,
72
- count: number?
73
- ): {
74
- entries: { { level: string, body: string, timestamp: number } },
75
- total: number,
76
- bufferCapacity: number,
77
- }
78
- local dir = direction or "tail"
68
+ --[=[
69
+ Returns up to `count` entries in chronological order. `"head"` takes the
70
+ oldest entries, `"tail"` (the default) the newest.
71
+ ]=]
72
+ function MessageBuffer.get(self: MessageBuffer, direction: Direction?, count: number?): BufferSnapshot
79
73
  local all = self:_toArray()
80
- local maxCount = count or #all
81
- local entries = {}
82
-
83
- if dir == "head" then
84
- -- Oldest first, take from the start
85
- for i = 1, math.min(maxCount, #all) do
86
- table.insert(entries, all[i])
87
- end
74
+ local takeCount = math.min(count or #all, #all)
75
+
76
+ local entries: { LogEntry }
77
+ if direction == "head" then
78
+ entries = table.move(all, 1, takeCount, 1, {})
88
79
  else
89
- -- Newest first (tail), take from the end
90
- local start = math.max(1, #all - maxCount + 1)
91
- for i = start, #all do
92
- table.insert(entries, all[i])
93
- end
80
+ entries = table.move(all, #all - takeCount + 1, #all, 1, {})
94
81
  end
95
82
 
96
83
  return {
@@ -100,45 +87,29 @@ function MessageBuffer.get(
100
87
  }
101
88
  end
102
89
 
103
- -- ---------------------------------------------------------------------------
104
- -- Clear the buffer
105
- -- ---------------------------------------------------------------------------
106
-
107
- function MessageBuffer.clear(self: any)
108
- self._buffer = {}
109
- self._head = 1
90
+ --[=[
91
+ Removes every entry.
92
+ ]=]
93
+ function MessageBuffer.Clear(self: MessageBuffer)
94
+ table.clear(self._buffer)
95
+ self._nextIndex = 1
110
96
  self._count = 0
111
97
  end
112
98
 
113
- -- ---------------------------------------------------------------------------
114
- -- Get the number of entries
115
- -- ---------------------------------------------------------------------------
116
-
117
- function MessageBuffer.size(self: any): number
99
+ --[=[
100
+ Returns the number of entries currently stored.
101
+ ]=]
102
+ function MessageBuffer.GetCount(self: MessageBuffer): number
118
103
  return self._count
119
104
  end
120
105
 
121
- -- ---------------------------------------------------------------------------
122
- -- Internal: convert ring buffer to a chronological array
123
- -- ---------------------------------------------------------------------------
124
-
125
- function MessageBuffer._toArray(self: any): { { level: string, body: string, timestamp: number } }
126
- local result = {}
106
+ function MessageBuffer._toArray(self: MessageBuffer): { LogEntry }
127
107
  if self._count < self._capacity then
128
- -- Buffer not full: entries are at indices 1..count
129
- for i = 1, self._count do
130
- table.insert(result, self._buffer[i])
131
- end
132
- else
133
- -- Buffer full: oldest entry is at _head, wrap around
134
- for i = self._head, self._capacity do
135
- table.insert(result, self._buffer[i])
136
- end
137
- for i = 1, self._head - 1 do
138
- table.insert(result, self._buffer[i])
139
- end
108
+ return table.move(self._buffer, 1, self._count, 1, {})
140
109
  end
141
- return result
110
+
111
+ local result = table.move(self._buffer, self._nextIndex, self._capacity, 1, {})
112
+ return table.move(self._buffer, 1, self._nextIndex - 1, #result + 1, result)
142
113
  end
143
114
 
144
115
  return MessageBuffer
@@ -0,0 +1,116 @@
1
+ --!strict
2
+ --[[
3
+ @class MessageBuffer.spec.lua
4
+ ]]
5
+
6
+ local require = require(script.Parent.loader).load(script)
7
+
8
+ local Jest = require("Jest")
9
+ local MessageBuffer = require("MessageBuffer")
10
+
11
+ local describe = Jest.Globals.describe
12
+ local expect = Jest.Globals.expect
13
+ local it = Jest.Globals.it
14
+
15
+ local function pushBodies(buffer: MessageBuffer.MessageBuffer, bodies: { string })
16
+ for index, body in bodies do
17
+ buffer:Push({ level = "Print", body = body, timestamp = index })
18
+ end
19
+ end
20
+
21
+ local function getBodies(entries: { MessageBuffer.LogEntry }): { string }
22
+ local bodies = {}
23
+ for _, entry in entries do
24
+ table.insert(bodies, entry.body)
25
+ end
26
+ return bodies
27
+ end
28
+
29
+ describe("MessageBuffer.new()", function()
30
+ it("should default to a capacity of 1000", function()
31
+ local buffer = MessageBuffer.new()
32
+
33
+ expect(buffer:get().bufferCapacity).toEqual(1000)
34
+ expect(buffer:GetCount()).toEqual(0)
35
+ end)
36
+ end)
37
+
38
+ describe("MessageBuffer:get()", function()
39
+ it("should return entries oldest first for either direction", function()
40
+ local buffer = MessageBuffer.new(10)
41
+ pushBodies(buffer, { "a", "b", "c", "d" })
42
+
43
+ expect(getBodies(buffer:get("head", 2).entries)).toEqual({ "a", "b" })
44
+ expect(getBodies(buffer:get("tail", 2).entries)).toEqual({ "c", "d" })
45
+ end)
46
+
47
+ it("should default to every entry from the tail", function()
48
+ local buffer = MessageBuffer.new(10)
49
+ pushBodies(buffer, { "a", "b", "c" })
50
+
51
+ expect(getBodies(buffer:get().entries)).toEqual({ "a", "b", "c" })
52
+ end)
53
+
54
+ it("should clamp a count larger than the buffer", function()
55
+ local buffer = MessageBuffer.new(10)
56
+ pushBodies(buffer, { "a", "b" })
57
+
58
+ expect(getBodies(buffer:get("head", 50).entries)).toEqual({ "a", "b" })
59
+ expect(getBodies(buffer:get("tail", 50).entries)).toEqual({ "a", "b" })
60
+ end)
61
+
62
+ it("should return nothing for a count of zero", function()
63
+ local buffer = MessageBuffer.new(10)
64
+ pushBodies(buffer, { "a", "b" })
65
+
66
+ expect(buffer:get("head", 0).entries).toEqual({})
67
+ expect(buffer:get("tail", 0).entries).toEqual({})
68
+ end)
69
+
70
+ it("should report the stored count and capacity", function()
71
+ local buffer = MessageBuffer.new(5)
72
+ pushBodies(buffer, { "a", "b", "c" })
73
+
74
+ local snapshot = buffer:get("tail", 1)
75
+
76
+ expect(snapshot.total).toEqual(3)
77
+ expect(snapshot.bufferCapacity).toEqual(5)
78
+ end)
79
+ end)
80
+
81
+ describe("MessageBuffer:Push()", function()
82
+ it("should overwrite the oldest entries once full", function()
83
+ local buffer = MessageBuffer.new(3)
84
+ pushBodies(buffer, { "a", "b", "c", "d", "e" })
85
+
86
+ expect(buffer:GetCount()).toEqual(3)
87
+ expect(getBodies(buffer:get().entries)).toEqual({ "c", "d", "e" })
88
+ end)
89
+
90
+ it("should keep chronological order after wrapping many times", function()
91
+ local buffer = MessageBuffer.new(4)
92
+ local bodies = {}
93
+ for index = 1, 23 do
94
+ table.insert(bodies, tostring(index))
95
+ end
96
+ pushBodies(buffer, bodies)
97
+
98
+ expect(getBodies(buffer:get().entries)).toEqual({ "20", "21", "22", "23" })
99
+ end)
100
+ end)
101
+
102
+ describe("MessageBuffer:Clear()", function()
103
+ it("should remove every entry and accept new ones", function()
104
+ local buffer = MessageBuffer.new(3)
105
+ pushBodies(buffer, { "a", "b", "c", "d" })
106
+
107
+ buffer:Clear()
108
+
109
+ expect(buffer:GetCount()).toEqual(0)
110
+ expect(buffer:get().entries).toEqual({})
111
+
112
+ pushBodies(buffer, { "x" })
113
+
114
+ expect(getBodies(buffer:get().entries)).toEqual({ "x" })
115
+ end)
116
+ end)
@@ -0,0 +1,233 @@
1
+ --!strict
2
+ --[=[
3
+ One open WebSocket to the bridge. Registers the plugin, dispatches incoming
4
+ messages through an [ActionRouter] and sends back the responses, and sends a
5
+ heartbeat every 15 seconds until destroyed.
6
+
7
+ A `shutdown` message closes the socket, and a closed socket destroys this.
8
+
9
+ A reply over `maxMessageBytes` (256 MiB by default) is replaced with a
10
+ `RESULT_TOO_LARGE` error for its request. Studio sends far larger messages,
11
+ but the bridge closes a socket that sends it more than it accepts, which
12
+ would fail every request in flight.
13
+
14
+ @class StudioBridgeConnection
15
+ ]=]
16
+
17
+ local HttpService = game:GetService("HttpService")
18
+
19
+ local StudioBridgeLogUtils = require(script.Parent.StudioBridgeLogUtils)
20
+
21
+ local HEARTBEAT_INTERVAL_SEC = 15
22
+ local DEFAULT_MAX_MESSAGE_BYTES = 256 * 1024 * 1024
23
+
24
+ local StudioBridgeConnection = {}
25
+ StudioBridgeConnection.ClassName = "StudioBridgeConnection"
26
+ StudioBridgeConnection.__index = StudioBridgeConnection
27
+
28
+ --[=[
29
+ The members of a WebStreamClient this uses. Specs pass a fake shaped like it.
30
+
31
+ @interface WebSocket
32
+ .Send (self, data: string) -> ()
33
+ .Close (self) -> ()
34
+ .MessageReceived RBXScriptSignal
35
+ .Closed RBXScriptSignal
36
+ @within StudioBridgeConnection
37
+ ]=]
38
+ export type WebSocket = {
39
+ Send: (self: any, data: string) -> (),
40
+ Close: (self: any) -> (),
41
+ MessageReceived: any,
42
+ Closed: any,
43
+ }
44
+
45
+ export type StudioBridgeConnectionOptions = {
46
+ webSocket: WebSocket,
47
+ router: any,
48
+ sessionId: string,
49
+ registerPayload: { [string]: any },
50
+ maxMessageBytes: number?,
51
+ }
52
+
53
+ export type StudioBridgeConnection = typeof(setmetatable(
54
+ {} :: {
55
+ _webSocket: WebSocket,
56
+ _router: any,
57
+ _sessionId: string,
58
+ _registerPayload: { [string]: any },
59
+ _maxMessageBytes: number,
60
+ _isOpen: boolean,
61
+ _startClock: number,
62
+ _signalConnections: { any },
63
+ _heartbeatThread: thread?,
64
+ },
65
+ {} :: typeof({ __index = StudioBridgeConnection })
66
+ ))
67
+
68
+ --[=[
69
+ Wires an opened WebSocket and sends the `register` message.
70
+ ]=]
71
+ function StudioBridgeConnection.new(options: StudioBridgeConnectionOptions): StudioBridgeConnection
72
+ local self: StudioBridgeConnection = setmetatable({} :: any, StudioBridgeConnection)
73
+
74
+ self._webSocket = assert(options.webSocket, "No webSocket")
75
+ self._router = assert(options.router, "No router")
76
+ self._sessionId = assert(options.sessionId, "No sessionId")
77
+ self._registerPayload = assert(options.registerPayload, "No registerPayload")
78
+ self._maxMessageBytes = options.maxMessageBytes or DEFAULT_MAX_MESSAGE_BYTES
79
+ self._isOpen = true
80
+ self._startClock = os.clock()
81
+ self._signalConnections = {}
82
+
83
+ table.insert(
84
+ self._signalConnections,
85
+ self._webSocket.MessageReceived:Connect(function(rawData: string)
86
+ self:_handleRawMessage(rawData)
87
+ end)
88
+ )
89
+ table.insert(
90
+ self._signalConnections,
91
+ self._webSocket.Closed:Connect(function()
92
+ self:Destroy()
93
+ end)
94
+ )
95
+
96
+ self:Send({
97
+ type = "register",
98
+ sessionId = self._sessionId,
99
+ payload = self._registerPayload,
100
+ })
101
+
102
+ self._heartbeatThread = task.spawn(function()
103
+ while self._isOpen do
104
+ self:_sendHeartbeat()
105
+ task.wait(HEARTBEAT_INTERVAL_SEC)
106
+ end
107
+ end)
108
+
109
+ return self
110
+ end
111
+
112
+ --[=[
113
+ Returns whether the socket is still open.
114
+ ]=]
115
+ function StudioBridgeConnection.IsOpen(self: StudioBridgeConnection): boolean
116
+ return self._isOpen
117
+ end
118
+
119
+ --[=[
120
+ Encodes and sends a message. Does nothing once the socket is closed, and
121
+ never throws, since action handlers call this from their own threads.
122
+ ]=]
123
+ function StudioBridgeConnection.Send(self: StudioBridgeConnection, message: { [string]: any })
124
+ if not self._isOpen then
125
+ return
126
+ end
127
+
128
+ local ok, err = pcall(function()
129
+ self._webSocket:Send(self:_encode(message))
130
+ end)
131
+ if not ok then
132
+ StudioBridgeLogUtils.warn(`Failed to send {tostring(message.type)}: {tostring(err)}`)
133
+ end
134
+ end
135
+
136
+ function StudioBridgeConnection._sendHeartbeat(self: StudioBridgeConnection)
137
+ self:Send({
138
+ type = "heartbeat",
139
+ sessionId = self._sessionId,
140
+ payload = {
141
+ uptimeMs = math.floor((os.clock() - self._startClock) * 1000),
142
+ state = self._registerPayload.state,
143
+ pendingRequests = 0,
144
+ },
145
+ })
146
+ end
147
+
148
+ function StudioBridgeConnection._handleRawMessage(self: StudioBridgeConnection, rawData: string)
149
+ local ok, err = pcall(StudioBridgeConnection._dispatchRawMessage, self, rawData)
150
+ if not ok then
151
+ StudioBridgeLogUtils.warn(`MessageReceived handler error: {tostring(err)}`)
152
+ end
153
+ end
154
+
155
+ function StudioBridgeConnection._dispatchRawMessage(self: StudioBridgeConnection, rawData: string)
156
+ local decodeOk, message = pcall(HttpService.JSONDecode, HttpService, rawData)
157
+ if not decodeOk or type(message) ~= "table" or type(message.type) ~= "string" then
158
+ StudioBridgeLogUtils.warn(`Failed to decode message: {string.sub(tostring(rawData), 1, 200)}`)
159
+ return
160
+ end
161
+
162
+ StudioBridgeLogUtils.logVerbose(
163
+ `Received: {message.type} (requestId={string.sub(tostring(message.requestId), 1, 8)}...)`
164
+ )
165
+
166
+ if message.type == "shutdown" then
167
+ self:_close()
168
+ return
169
+ end
170
+
171
+ local response = self._router:Dispatch(message)
172
+ if not response then
173
+ StudioBridgeLogUtils.logVerbose(`No response for: {message.type}`)
174
+ return
175
+ end
176
+
177
+ local encoded = self:_encode(response)
178
+ StudioBridgeLogUtils.logVerbose(`Sending: {response.type} ({#encoded} bytes)`)
179
+ self._webSocket:Send(encoded)
180
+ end
181
+
182
+ --[[
183
+ Encodes `message`, or a RESULT_TOO_LARGE error for its request when it is
184
+ over the limit.
185
+ ]]
186
+ function StudioBridgeConnection._encode(self: StudioBridgeConnection, message: { [string]: any }): string
187
+ local encoded = HttpService:JSONEncode(message)
188
+ if #encoded <= self._maxMessageBytes or message.requestId == nil then
189
+ return encoded
190
+ end
191
+
192
+ local megabytes = function(bytes: number): string
193
+ return string.format("%.1f MB", bytes / (1024 * 1024))
194
+ end
195
+ return HttpService:JSONEncode({
196
+ type = "error",
197
+ sessionId = message.sessionId,
198
+ requestId = message.requestId,
199
+ payload = {
200
+ code = "RESULT_TOO_LARGE",
201
+ message = `The {message.type} reply is {megabytes(#encoded)}, over the {megabytes(self._maxMessageBytes)} a reply can carry. Return less, or narrow the request.`,
202
+ },
203
+ })
204
+ end
205
+
206
+ function StudioBridgeConnection._close(self: StudioBridgeConnection)
207
+ self._isOpen = false
208
+ pcall(function()
209
+ self._webSocket:Close()
210
+ end)
211
+ end
212
+
213
+ --[=[
214
+ Stops the heartbeat and stops listening to the socket. Does not close the
215
+ socket, since by then it is usually already closed.
216
+ ]=]
217
+ function StudioBridgeConnection.Destroy(self: StudioBridgeConnection)
218
+ self._isOpen = false
219
+ StudioBridgeLogUtils.setVerbose(false)
220
+
221
+ for _, connection in self._signalConnections do
222
+ connection:Disconnect()
223
+ end
224
+ table.clear(self._signalConnections)
225
+
226
+ local heartbeatThread = self._heartbeatThread
227
+ self._heartbeatThread = nil
228
+ if heartbeatThread and coroutine.status(heartbeatThread) == "suspended" then
229
+ task.cancel(heartbeatThread)
230
+ end
231
+ end
232
+
233
+ return StudioBridgeConnection