@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,76 +1,109 @@
1
- --[[
2
- QueryLogs action handler for the studio-bridge plugin.
1
+ --!strict
2
+ --[=[
3
+ Action that answers `queryLogs` from the plugin's log buffer, filtered by
4
+ level and by `since` (epoch seconds), then trimmed to `count` entries from
5
+ the `head` or `tail`. `[StudioBridge]` messages are left out unless
6
+ `includeInternal` is set. With `groupRepeats`, identical consecutive
7
+ entries become one with `repeats` and `lastTimestamp`, before `count`
8
+ applies, so `count` counts the grouped lines.
3
9
 
4
- Reads from a MessageBuffer instance and returns filtered log entries.
5
- Supports direction (head/tail), count, level filtering, and
6
- includeInternal (to show/hide [StudioBridge] messages).
10
+ Request: `{ type: "queryLogs", payload: { count?, direction?, levels?, includeInternal?, since?, groupRepeats? } }`
11
+ Response: `{ type: "logsResult", payload: { entries, total, bufferCapacity } }`
7
12
 
8
- Protocol:
9
- Request: { type: "queryLogs", payload: { count?, direction?, levels?, includeInternal? } }
10
- Response: { type: "logsResult", payload: { entries, total, bufferCapacity } }
11
- ]]
13
+ @class QueryLogsAction
14
+ ]=]
15
+
16
+ local DEFAULT_COUNT = 50
17
+ local INTERNAL_PREFIX = "[StudioBridge]"
12
18
 
13
19
  local QueryLogsAction = {}
14
20
 
15
- -- ---------------------------------------------------------------------------
16
- -- Public API
17
- -- ---------------------------------------------------------------------------
21
+ type LogEntry = {
22
+ level: string,
23
+ body: string,
24
+ timestamp: number,
25
+ repeats: number?,
26
+ lastTimestamp: number?,
27
+ }
18
28
 
19
- -- Register this handler with the ActionRouter.
29
+ --[=[
30
+ Registers the `queryLogs` handler against `logBuffer`.
31
+ ]=]
20
32
  function QueryLogsAction.register(router: any, _sendMessage: any, logBuffer: any)
21
33
  router:setResponseType("queryLogs", "logsResult")
22
34
  router:register("queryLogs", function(payload: { [string]: any }, _requestId: string, _sessionId: string)
23
35
  local direction = payload.direction or "tail"
24
- local count = payload.count or 50
25
- local levels = payload.levels -- nil means all levels
26
- local includeInternal = payload.includeInternal == true
27
-
28
- -- Get raw entries from the buffer
29
- local result = logBuffer:get(direction, nil) -- get all, then filter
30
-
31
- local filtered = {}
32
- for _, entry in result.entries do
33
- -- Filter by includeInternal
34
- if not includeInternal and string.sub(entry.body, 1, 14) == "[StudioBridge]" then
35
- continue
36
- end
36
+ local count = payload.count or DEFAULT_COUNT
37
37
 
38
- -- Filter by levels
39
- if levels and #levels > 0 then
40
- local matched = false
41
- for _, level in levels do
42
- if entry.level == level then
43
- matched = true
44
- break
45
- end
46
- end
47
- if not matched then
48
- continue
49
- end
50
- end
51
-
52
- table.insert(filtered, entry)
38
+ local snapshot = logBuffer:get(direction, nil)
39
+ local filtered = QueryLogsAction._filterEntries(
40
+ snapshot.entries,
41
+ payload.levels,
42
+ payload.includeInternal == true,
43
+ if type(payload.since) == "number" then payload.since else nil
44
+ )
45
+ if payload.groupRepeats == true then
46
+ filtered = QueryLogsAction._groupRepeats(filtered)
53
47
  end
54
48
 
55
- -- Apply direction and count to filtered results
56
- local entries = {}
49
+ local takeCount = math.min(count, #filtered)
50
+ local entries: { LogEntry }
57
51
  if direction == "head" then
58
- for i = 1, math.min(count, #filtered) do
59
- table.insert(entries, filtered[i])
60
- end
52
+ entries = table.move(filtered, 1, takeCount, 1, {})
61
53
  else
62
- local start = math.max(1, #filtered - count + 1)
63
- for i = start, #filtered do
64
- table.insert(entries, filtered[i])
65
- end
54
+ entries = table.move(filtered, #filtered - takeCount + 1, #filtered, 1, {})
66
55
  end
67
56
 
68
57
  return {
69
58
  entries = entries,
70
- total = result.total,
71
- bufferCapacity = result.bufferCapacity,
59
+ total = snapshot.total,
60
+ bufferCapacity = snapshot.bufferCapacity,
72
61
  }
73
62
  end)
74
63
  end
75
64
 
65
+ --[[
66
+ Collapses runs of entries with the same level and body into the first,
67
+ with how many there were and when the last was logged.
68
+ ]]
69
+ function QueryLogsAction._groupRepeats(entries: { LogEntry }): { LogEntry }
70
+ local grouped: { LogEntry } = {}
71
+ local last: LogEntry? = nil
72
+ for _, entry in entries do
73
+ if last and last.level == entry.level and last.body == entry.body then
74
+ last.repeats = (last.repeats or 1) + 1
75
+ last.lastTimestamp = entry.timestamp
76
+ else
77
+ last = table.clone(entry)
78
+ table.insert(grouped, last :: LogEntry)
79
+ end
80
+ end
81
+ return grouped
82
+ end
83
+
84
+ function QueryLogsAction._filterEntries(
85
+ entries: { LogEntry },
86
+ levels: { string }?,
87
+ includeInternal: boolean,
88
+ since: number?
89
+ ): { LogEntry }
90
+ local filtered: { LogEntry } = {}
91
+ for _, entry in entries do
92
+ if not includeInternal and string.sub(entry.body, 1, #INTERNAL_PREFIX) == INTERNAL_PREFIX then
93
+ continue
94
+ end
95
+
96
+ if levels and #levels > 0 and not table.find(levels, entry.level) then
97
+ continue
98
+ end
99
+
100
+ if since and entry.timestamp < since then
101
+ continue
102
+ end
103
+
104
+ table.insert(filtered, entry)
105
+ end
106
+ return filtered
107
+ end
108
+
76
109
  return QueryLogsAction
@@ -0,0 +1,132 @@
1
+ --!strict
2
+ --[[
3
+ @class query-logs.spec.lua
4
+ ]]
5
+
6
+ local require = require(script.Parent.loader).load(script)
7
+
8
+ local ActionRouter = require("ActionRouter")
9
+ local Jest = require("Jest")
10
+ local MessageBuffer = require("MessageBuffer")
11
+ local QueryLogsAction = require("query-logs")
12
+
13
+ local describe = Jest.Globals.describe
14
+ local expect = Jest.Globals.expect
15
+ local it = Jest.Globals.it
16
+
17
+ local function setup(entries: { { level: string, body: string } }): any
18
+ local router = ActionRouter.new()
19
+ local logBuffer = MessageBuffer.new(100)
20
+ for index, entry in entries do
21
+ logBuffer:Push({ level = entry.level, body = entry.body, timestamp = index })
22
+ end
23
+
24
+ QueryLogsAction.register(router, nil, logBuffer)
25
+
26
+ return {
27
+ query = function(payload: { [string]: any }): any
28
+ local response = router:Dispatch({ type = "queryLogs", sessionId = "session-1", payload = payload })
29
+ assert(response, "No response")
30
+ return response
31
+ end,
32
+ }
33
+ end
34
+
35
+ local function getBodies(response: any): { string }
36
+ local bodies = {}
37
+ for _, entry in response.payload.entries do
38
+ table.insert(bodies, entry.body)
39
+ end
40
+ return bodies
41
+ end
42
+
43
+ local MIXED_ENTRIES = {
44
+ { level = "Print", body = "one" },
45
+ { level = "Warning", body = "two" },
46
+ { level = "Error", body = "three" },
47
+ { level = "Print", body = "[StudioBridge] internal" },
48
+ { level = "Print", body = "four" },
49
+ }
50
+
51
+ describe("queryLogs", function()
52
+ it("should leave out entries logged before since, then apply count", function()
53
+ local controller = setup(MIXED_ENTRIES)
54
+
55
+ local response = controller.query({ since = 2, count = 2, direction = "head" })
56
+
57
+ expect(getBodies(response)).toEqual({ "two", "three" })
58
+ end)
59
+
60
+ it("should group repeats before applying count", function()
61
+ local controller = setup({
62
+ { level = "Print", body = "start" },
63
+ { level = "Print", body = "tick" },
64
+ { level = "Print", body = "tick" },
65
+ { level = "Print", body = "tick" },
66
+ { level = "Warning", body = "tick" },
67
+ })
68
+
69
+ local response = controller.query({ groupRepeats = true, count = 2 })
70
+
71
+ expect(response.payload.entries).toEqual({
72
+ { level = "Print", body = "tick", timestamp = 2, repeats = 3, lastTimestamp = 4 },
73
+ { level = "Warning", body = "tick", timestamp = 5 },
74
+ })
75
+ end)
76
+
77
+ it("should answer logsResult with the buffer's total and capacity", function()
78
+ local controller = setup(MIXED_ENTRIES)
79
+
80
+ local response = controller.query({})
81
+
82
+ expect(response.type).toEqual("logsResult")
83
+ expect(response.payload.total).toEqual(5)
84
+ expect(response.payload.bufferCapacity).toEqual(100)
85
+ end)
86
+
87
+ it("should return the newest entries by default, oldest first", function()
88
+ local controller = setup(MIXED_ENTRIES)
89
+
90
+ expect(getBodies(controller.query({ count = 2 }))).toEqual({ "three", "four" })
91
+ end)
92
+
93
+ it("should return the oldest entries for head", function()
94
+ local controller = setup(MIXED_ENTRIES)
95
+
96
+ expect(getBodies(controller.query({ direction = "head", count = 2 }))).toEqual({ "one", "two" })
97
+ end)
98
+
99
+ it("should keep only the requested levels", function()
100
+ local controller = setup(MIXED_ENTRIES)
101
+
102
+ expect(getBodies(controller.query({ levels = { "Warning", "Error" } }))).toEqual({ "two", "three" })
103
+ end)
104
+
105
+ it("should leave out [StudioBridge] messages unless asked", function()
106
+ local controller = setup(MIXED_ENTRIES)
107
+
108
+ expect(getBodies(controller.query({}))).toEqual({ "one", "two", "three", "four" })
109
+ expect(getBodies(controller.query({ includeInternal = true }))).toEqual({
110
+ "one",
111
+ "two",
112
+ "three",
113
+ "[StudioBridge] internal",
114
+ "four",
115
+ })
116
+ end)
117
+
118
+ it("should count after filtering", function()
119
+ local controller = setup(MIXED_ENTRIES)
120
+
121
+ expect(getBodies(controller.query({ levels = { "Print" }, count = 1 }))).toEqual({ "four" })
122
+ end)
123
+
124
+ it("should return no entries for an empty buffer", function()
125
+ local controller = setup({})
126
+
127
+ local response = controller.query({})
128
+
129
+ expect(response.payload.entries).toEqual({})
130
+ expect(response.payload.total).toEqual(0)
131
+ end)
132
+ end)
@@ -0,0 +1,108 @@
1
+ --!strict
2
+ --[=[
3
+ Action that serializes the instances at the given paths into one binary
4
+ model (.rbxm) with SerializationService and answers with it as base64.
5
+
6
+ Request: `{ type: "exportInstances", payload: { paths: { string } } }`
7
+ Response: `{ type: "exportInstancesResult", payload: { data, paths } }`
8
+
9
+ @class ExportInstancesAction
10
+ ]=]
11
+
12
+ local ExportInstancesAction = {}
13
+
14
+ --[=[
15
+ Registers the `exportInstances` handler.
16
+ ]=]
17
+ function ExportInstancesAction.register(router: any)
18
+ router:setResponseType("exportInstances", "exportInstancesResult")
19
+ router:register(
20
+ "exportInstances",
21
+ function(payload: { [string]: any }, _requestId: string, _sessionId: string): { [string]: any }?
22
+ local paths = payload.paths
23
+ if type(paths) ~= "table" or #paths == 0 then
24
+ return {
25
+ success = false,
26
+ code = "INVALID_PAYLOAD",
27
+ message = "exportInstances needs at least one path",
28
+ }
29
+ end
30
+
31
+ local instances = {}
32
+ local fullNames = {}
33
+ for _, path in paths do
34
+ local instance = ExportInstancesAction._resolveInstance(path)
35
+ if not instance then
36
+ return {
37
+ success = false,
38
+ code = "INSTANCE_NOT_FOUND",
39
+ message = `Instance not found at path: {path}`,
40
+ }
41
+ end
42
+ table.insert(instances, instance)
43
+ table.insert(fullNames, instance:GetFullName())
44
+ end
45
+
46
+ local serializeOk, serialized = pcall(function()
47
+ return game:GetService("SerializationService"):SerializeInstancesAsync(instances)
48
+ end)
49
+ if not serializeOk then
50
+ return {
51
+ success = false,
52
+ code = "SERIALIZE_FAILED",
53
+ message = `Failed to serialize instances: {serialized}`,
54
+ }
55
+ end
56
+
57
+ local encodeOk, encoded = pcall(function()
58
+ return buffer.tostring(game:GetService("EncodingService"):Base64Encode(serialized))
59
+ end)
60
+ if not encodeOk then
61
+ return {
62
+ success = false,
63
+ code = "ENCODE_FAILED",
64
+ message = `Failed to base64 encode data: {encoded}`,
65
+ }
66
+ end
67
+
68
+ return {
69
+ data = encoded,
70
+ paths = fullNames,
71
+ }
72
+ end
73
+ )
74
+ end
75
+
76
+ --[[
77
+ Walks `path` down from game, falling back to GetService for top-level names
78
+ that are not children by that name. A leading "game" is optional.
79
+ ]]
80
+ function ExportInstancesAction._resolveInstance(path: string): Instance?
81
+ local parts = string.split(path, ".")
82
+ local startIndex = if parts[1] == "game" then 2 else 1
83
+
84
+ local current: Instance = game
85
+ for index = startIndex, #parts do
86
+ local part = parts[index]
87
+
88
+ local ok, child = pcall(current.FindFirstChild, current, part)
89
+ if ok and child then
90
+ current = child
91
+ continue
92
+ end
93
+
94
+ if current ~= game then
95
+ return nil
96
+ end
97
+
98
+ local serviceOk, service = pcall(game.GetService, game, part)
99
+ if not (serviceOk and service) then
100
+ return nil
101
+ end
102
+ current = service
103
+ end
104
+
105
+ return current
106
+ end
107
+
108
+ return ExportInstancesAction
@@ -0,0 +1,87 @@
1
+ --!strict
2
+ --[[
3
+ @class export-instances.spec.lua
4
+ ]]
5
+
6
+ local require = require(script.Parent.loader).load(script)
7
+
8
+ local HttpService = game:GetService("HttpService")
9
+
10
+ local ActionRouter = require("ActionRouter")
11
+ local ExportInstancesAction = require("export-instances")
12
+ local Jest = require("Jest")
13
+
14
+ local describe = Jest.Globals.describe
15
+ local expect = Jest.Globals.expect
16
+ local it = Jest.Globals.it
17
+
18
+ local function setup(): any
19
+ local router = ActionRouter.new()
20
+ ExportInstancesAction.register(router)
21
+
22
+ local root = Instance.new("Folder")
23
+ root.Name = `ExportInstancesSpec{HttpService:GenerateGUID(false)}`
24
+
25
+ local value = Instance.new("IntValue")
26
+ value.Name = "Score"
27
+ value.Value = 42
28
+ value.Parent = root
29
+
30
+ root.Parent = workspace
31
+
32
+ local controller = {
33
+ root = root,
34
+ rootPath = `Workspace.{root.Name}`,
35
+ export = function(payload: { [string]: any }): any
36
+ return router:Dispatch({ type = "exportInstances", sessionId = "session-1", payload = payload })
37
+ end,
38
+ Destroy = function(_self)
39
+ root:Destroy()
40
+ end,
41
+ }
42
+
43
+ return controller
44
+ end
45
+
46
+ describe("exportInstances", function()
47
+ it("should answer with a binary model that deserializes to the same instances", function()
48
+ local controller = setup()
49
+
50
+ local response = controller.export({ paths = { controller.rootPath } })
51
+
52
+ expect(response.type).toEqual("exportInstancesResult")
53
+ expect(response.payload.paths).toEqual({ controller.root:GetFullName() })
54
+
55
+ local decoded = game:GetService("EncodingService"):Base64Decode(buffer.fromstring(response.payload.data))
56
+ expect(buffer.readstring(decoded, 0, 8)).toEqual("<roblox!")
57
+
58
+ local instances = game:GetService("SerializationService"):DeserializeInstancesAsync(decoded)
59
+ expect(#instances).toEqual(1)
60
+ expect(instances[1].Name).toEqual(controller.root.Name)
61
+ expect((instances[1]:FindFirstChild("Score") :: IntValue).Value).toEqual(42)
62
+
63
+ controller:Destroy()
64
+ end)
65
+
66
+ it("should fail when a path does not resolve", function()
67
+ local controller = setup()
68
+
69
+ local response = controller.export({ paths = { controller.rootPath, "Workspace.DoesNotExist" } })
70
+
71
+ expect(response.type).toEqual("error")
72
+ expect(response.payload.code).toEqual("INSTANCE_NOT_FOUND")
73
+
74
+ controller:Destroy()
75
+ end)
76
+
77
+ it("should fail without paths", function()
78
+ local controller = setup()
79
+
80
+ local response = controller.export({ paths = {} })
81
+
82
+ expect(response.type).toEqual("error")
83
+ expect(response.payload.code).toEqual("INVALID_PAYLOAD")
84
+
85
+ controller:Destroy()
86
+ end)
87
+ end)
@@ -0,0 +1,27 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { exportInstancesHandlerAsync } from './export.js';
3
+
4
+ describe('exportInstancesHandlerAsync', () => {
5
+ it('returns the model with its size and the exported paths', async () => {
6
+ const data = Buffer.from('<roblox!model').toString('base64');
7
+ const session = {
8
+ exportInstancesAsync: vi
9
+ .fn()
10
+ .mockResolvedValue({ data, paths: ['Workspace.Map'] }),
11
+ } as any;
12
+
13
+ const result = await exportInstancesHandlerAsync(session, [
14
+ 'Workspace.Map',
15
+ ]);
16
+
17
+ expect(session.exportInstancesAsync).toHaveBeenCalledWith([
18
+ 'Workspace.Map',
19
+ ]);
20
+ expect(result).toEqual({
21
+ data,
22
+ paths: ['Workspace.Map'],
23
+ bytes: 13,
24
+ summary: 'Exported 1 instance(s) (13 bytes): Workspace.Map',
25
+ });
26
+ });
27
+ });
@@ -0,0 +1,60 @@
1
+ /**
2
+ * `explorer export` — serialize instances from a connected Studio session
3
+ * into a binary model (.rbxm).
4
+ */
5
+
6
+ import { defineCommand } from '../../framework/define-command.js';
7
+ import { arg } from '../../framework/arg-builder.js';
8
+ import type { BridgeSession } from '../../../bridge/index.js';
9
+
10
+ export interface ExportResult {
11
+ /** The binary model (.rbxm), base64-encoded. */
12
+ data: string;
13
+ paths: string[];
14
+ bytes: number;
15
+ summary: string;
16
+ }
17
+
18
+ interface ExportArgs {
19
+ paths: string[];
20
+ }
21
+
22
+ export async function exportInstancesHandlerAsync(
23
+ session: BridgeSession,
24
+ paths: string[]
25
+ ): Promise<ExportResult> {
26
+ const result = await session.exportInstancesAsync(paths);
27
+ const bytes = Buffer.from(result.data, 'base64').length;
28
+
29
+ return {
30
+ data: result.data,
31
+ paths: result.paths,
32
+ bytes,
33
+ summary: `Exported ${
34
+ result.paths.length
35
+ } instance(s) (${bytes} bytes): ${result.paths.join(', ')}`,
36
+ };
37
+ }
38
+
39
+ export const exportCommand = defineCommand<ExportArgs, ExportResult>({
40
+ group: 'explorer',
41
+ name: 'export',
42
+ description:
43
+ 'Save instances from Studio as a binary model (.rbxm). Use -o to write the file.',
44
+ category: 'execution',
45
+ safety: 'read',
46
+ scope: 'session',
47
+ args: {
48
+ paths: arg.positional({
49
+ description: 'Dot-separated instance paths (e.g. Workspace.Map)',
50
+ required: true,
51
+ array: true,
52
+ }),
53
+ },
54
+ cli: {
55
+ binaryField: 'data',
56
+ format: (result) => result.summary,
57
+ },
58
+ handler: async (session, args) =>
59
+ exportInstancesHandlerAsync(session, args.paths),
60
+ });