@quenty/studio-bridge 0.21.1 → 0.22.1

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 +10 -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
@@ -0,0 +1,174 @@
1
+ --!strict
2
+ --[=[
3
+ Action that searches the DataModel for instances by name, class and tag,
4
+ answering with the first `limit` matches.
5
+
6
+ - `name` matches case-insensitively anywhere in the instance's name, or the
7
+ whole name with `exactName`.
8
+ - `className` matches with IsA, so `BasePart` finds every part.
9
+ - Without `root`, searches every service the plugin can read, other than
10
+ Studio's own (CoreGui and the like).
11
+
12
+ Request: `{ type: "findInstances", payload: { root?, name?, exactName?, className?, tag?, limit? } }`
13
+ Response: `{ type: "findInstancesResult", payload: { instances, searched, truncated } }`
14
+
15
+ @class FindInstancesAction
16
+ ]=]
17
+
18
+ local DEFAULT_LIMIT = 200
19
+ local YIELD_EVERY = 5000
20
+ -- Studio's own instances, which the place does not contain
21
+ local ENGINE_SERVICES = table.freeze({
22
+ CoreGui = true,
23
+ CorePackages = true,
24
+ RobloxReplicatedStorage = true,
25
+ })
26
+
27
+ local FindInstancesAction = {}
28
+
29
+ type FoundInstance = {
30
+ path: string,
31
+ name: string,
32
+ className: string,
33
+ }
34
+
35
+ --[=[
36
+ Registers the `findInstances` handler.
37
+ ]=]
38
+ function FindInstancesAction.register(router: any)
39
+ router:setResponseType("findInstances", "findInstancesResult")
40
+ router:register(
41
+ "findInstances",
42
+ function(payload: { [string]: any }, _requestId: string, _sessionId: string): { [string]: any }?
43
+ local root: Instance? = game
44
+ if type(payload.root) == "string" then
45
+ root = FindInstancesAction._resolveInstance(payload.root)
46
+ if not root then
47
+ return {
48
+ success = false,
49
+ code = "INSTANCE_NOT_FOUND",
50
+ message = `Instance not found at path: {payload.root}`,
51
+ }
52
+ end
53
+ end
54
+
55
+ local matches = FindInstancesAction._createMatcher(payload)
56
+ local limit = if type(payload.limit) == "number" then payload.limit else DEFAULT_LIMIT
57
+
58
+ local found: { FoundInstance } = {}
59
+ local searched = 0
60
+ local truncated = false
61
+ for _, instance in FindInstancesAction._getSearchable(root :: Instance) do
62
+ searched += 1
63
+ if searched % YIELD_EVERY == 0 then
64
+ task.wait()
65
+ end
66
+
67
+ if matches(instance) then
68
+ if #found >= limit then
69
+ truncated = true
70
+ break
71
+ end
72
+ table.insert(found, {
73
+ path = instance:GetFullName(),
74
+ name = instance.Name,
75
+ className = instance.ClassName,
76
+ })
77
+ end
78
+ end
79
+
80
+ return {
81
+ instances = found,
82
+ searched = searched,
83
+ truncated = truncated,
84
+ }
85
+ end
86
+ )
87
+ end
88
+
89
+ function FindInstancesAction._createMatcher(payload: { [string]: any }): (Instance) -> boolean
90
+ local name = if type(payload.name) == "string" and payload.name ~= "" then string.lower(payload.name) else nil
91
+ local exactName = payload.exactName == true
92
+ local className = if type(payload.className) == "string" then payload.className else nil
93
+ local tag = if type(payload.tag) == "string" then payload.tag else nil
94
+
95
+ return function(instance: Instance): boolean
96
+ if name then
97
+ local instanceName = string.lower(instance.Name)
98
+ if exactName then
99
+ if instanceName ~= name then
100
+ return false
101
+ end
102
+ elseif not string.find(instanceName, name, 1, true) then
103
+ return false
104
+ end
105
+ end
106
+
107
+ if className and not instance:IsA(className) then
108
+ return false
109
+ end
110
+
111
+ if tag and not instance:HasTag(tag) then
112
+ return false
113
+ end
114
+
115
+ return true
116
+ end
117
+ end
118
+
119
+ --[[
120
+ Returns the descendants of `root`. For game, that is the descendants of
121
+ each place service the plugin may read, since some refuse access.
122
+ ]]
123
+ function FindInstancesAction._getSearchable(root: Instance): { Instance }
124
+ if root ~= game then
125
+ return root:GetDescendants()
126
+ end
127
+
128
+ local searchable = {}
129
+ for _, service in game:GetChildren() do
130
+ if ENGINE_SERVICES[service.ClassName] then
131
+ continue
132
+ end
133
+ local ok, descendants = pcall(service.GetDescendants, service)
134
+ if ok then
135
+ table.insert(searchable, service)
136
+ table.move(descendants, 1, #descendants, #searchable + 1, searchable)
137
+ end
138
+ end
139
+ return searchable
140
+ end
141
+
142
+ --[[
143
+ Walks `path` down from game, falling back to GetService for top-level names
144
+ that are not children by that name. A leading "game" is optional.
145
+ ]]
146
+ function FindInstancesAction._resolveInstance(path: string): Instance?
147
+ local parts = string.split(path, ".")
148
+ local startIndex = if parts[1] == "game" then 2 else 1
149
+
150
+ local current: Instance = game
151
+ for index = startIndex, #parts do
152
+ local part = parts[index]
153
+
154
+ local ok, child = pcall(current.FindFirstChild, current, part)
155
+ if ok and child then
156
+ current = child
157
+ continue
158
+ end
159
+
160
+ if current ~= game then
161
+ return nil
162
+ end
163
+
164
+ local serviceOk, service = pcall(game.GetService, game, part)
165
+ if not (serviceOk and service) then
166
+ return nil
167
+ end
168
+ current = service
169
+ end
170
+
171
+ return current
172
+ end
173
+
174
+ return FindInstancesAction
@@ -0,0 +1,122 @@
1
+ --!strict
2
+ --[[
3
+ @class find-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 FindInstancesAction = require("find-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
+ FindInstancesAction.register(router)
21
+
22
+ local root = Instance.new("Folder")
23
+ root.Name = `FindInstancesSpec{HttpService:GenerateGUID(false)}`
24
+
25
+ local spawnPart = Instance.new("Part")
26
+ spawnPart.Name = "RedSpawn"
27
+ spawnPart:AddTag("Spawn")
28
+ spawnPart.Parent = root
29
+
30
+ local wedge = Instance.new("WedgePart")
31
+ wedge.Name = "BlueSpawn"
32
+ wedge.Parent = root
33
+
34
+ local value = Instance.new("StringValue")
35
+ value.Name = "SpawnName"
36
+ value.Parent = root
37
+
38
+ root.Parent = workspace
39
+
40
+ local controller = {
41
+ root = root,
42
+ rootPath = `Workspace.{root.Name}`,
43
+ find = function(payload: { [string]: any }): any
44
+ return router:Dispatch({ type = "findInstances", sessionId = "session-1", payload = payload })
45
+ end,
46
+ Destroy = function(_self)
47
+ root:Destroy()
48
+ end,
49
+ }
50
+
51
+ return controller
52
+ end
53
+
54
+ local function getNames(response: any): { string }
55
+ local names = {}
56
+ for _, found in response.payload.instances do
57
+ table.insert(names, found.name)
58
+ end
59
+ table.sort(names)
60
+ return names
61
+ end
62
+
63
+ describe("findInstances", function()
64
+ it("should match names case-insensitively anywhere in the name", function()
65
+ local controller = setup()
66
+
67
+ local response = controller.find({ root = controller.rootPath, name = "spawn" })
68
+
69
+ expect(response.type).toEqual("findInstancesResult")
70
+ expect(getNames(response)).toEqual({ "BlueSpawn", "RedSpawn", "SpawnName" })
71
+ controller:Destroy()
72
+ end)
73
+
74
+ it("should match the whole name with exactName", function()
75
+ local controller = setup()
76
+
77
+ local response = controller.find({ root = controller.rootPath, name = "redspawn", exactName = true })
78
+
79
+ expect(getNames(response)).toEqual({ "RedSpawn" })
80
+ expect(response.payload.instances[1].path).toEqual(`{controller.root:GetFullName()}.RedSpawn`)
81
+ expect(response.payload.instances[1].className).toEqual("Part")
82
+ controller:Destroy()
83
+ end)
84
+
85
+ it("should match a class and its subclasses", function()
86
+ local controller = setup()
87
+
88
+ local response = controller.find({ root = controller.rootPath, className = "BasePart" })
89
+
90
+ expect(getNames(response)).toEqual({ "BlueSpawn", "RedSpawn" })
91
+ controller:Destroy()
92
+ end)
93
+
94
+ it("should match a tag", function()
95
+ local controller = setup()
96
+
97
+ local response = controller.find({ root = controller.rootPath, tag = "Spawn" })
98
+
99
+ expect(getNames(response)).toEqual({ "RedSpawn" })
100
+ controller:Destroy()
101
+ end)
102
+
103
+ it("should stop at the limit and say so", function()
104
+ local controller = setup()
105
+
106
+ local response = controller.find({ root = controller.rootPath, name = "spawn", limit = 2 })
107
+
108
+ expect(#response.payload.instances).toEqual(2)
109
+ expect(response.payload.truncated).toEqual(true)
110
+ controller:Destroy()
111
+ end)
112
+
113
+ it("should fail when the root does not resolve", function()
114
+ local controller = setup()
115
+
116
+ local response = controller.find({ root = "Workspace.DoesNotExist" })
117
+
118
+ expect(response.type).toEqual("error")
119
+ expect(response.payload.code).toEqual("INSTANCE_NOT_FOUND")
120
+ controller:Destroy()
121
+ end)
122
+ end)
@@ -0,0 +1,53 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { findInstancesHandlerAsync, formatFindText } from './find.js';
3
+
4
+ function createSession(result: unknown) {
5
+ return { findInstancesAsync: vi.fn().mockResolvedValue(result) } as any;
6
+ }
7
+
8
+ describe('findInstancesHandlerAsync', () => {
9
+ it('passes the search through and summarizes it', async () => {
10
+ const session = createSession({
11
+ instances: [
12
+ { path: 'Workspace.RedSpawn', name: 'RedSpawn', className: 'Part' },
13
+ ],
14
+ searched: 120,
15
+ truncated: false,
16
+ });
17
+
18
+ const result = await findInstancesHandlerAsync(session, {
19
+ name: 'spawn',
20
+ className: 'BasePart',
21
+ exact: true,
22
+ });
23
+
24
+ expect(session.findInstancesAsync).toHaveBeenCalledWith({
25
+ name: 'spawn',
26
+ className: 'BasePart',
27
+ exactName: true,
28
+ root: undefined,
29
+ tag: undefined,
30
+ limit: undefined,
31
+ });
32
+ expect(result.summary).toBe('1 match(es) among 120 instances');
33
+ expect(formatFindText(result)).toContain('Workspace.RedSpawn');
34
+ });
35
+
36
+ it('says when it stopped at the limit', async () => {
37
+ const session = createSession({
38
+ instances: [{ path: 'Workspace.A', name: 'A', className: 'Part' }],
39
+ searched: 10,
40
+ truncated: true,
41
+ });
42
+
43
+ const result = await findInstancesHandlerAsync(session, { tag: 'Spawn' });
44
+
45
+ expect(result.summary).toBe('First 1 matches; raise --limit to see more');
46
+ });
47
+
48
+ it('needs something to search for', async () => {
49
+ await expect(
50
+ findInstancesHandlerAsync(createSession({}), {})
51
+ ).rejects.toThrow('Give a name, --class or --tag to search for');
52
+ });
53
+ });
@@ -0,0 +1,113 @@
1
+ /**
2
+ * `explorer find` — search the DataModel for instances by name, class and tag.
3
+ */
4
+
5
+ import { OutputHelper } from '@quenty/cli-output-helpers';
6
+ import {
7
+ formatTable,
8
+ type TableColumn,
9
+ } from '@quenty/cli-output-helpers/reporting';
10
+ import { defineCommand } from '../../framework/define-command.js';
11
+ import { arg } from '../../framework/arg-builder.js';
12
+ import type { BridgeSession } from '../../../bridge/index.js';
13
+ import type { FoundInstance } from '../../../server/web-socket-protocol.js';
14
+
15
+ export interface FindOptions {
16
+ name?: string;
17
+ exact?: boolean;
18
+ className?: string;
19
+ tag?: string;
20
+ root?: string;
21
+ limit?: number;
22
+ }
23
+
24
+ export interface FindResult {
25
+ instances: FoundInstance[];
26
+ searched: number;
27
+ /** True when the search stopped at --limit; there may be more. */
28
+ truncated: boolean;
29
+ summary: string;
30
+ }
31
+
32
+ export async function findInstancesHandlerAsync(
33
+ session: BridgeSession,
34
+ options: FindOptions
35
+ ): Promise<FindResult> {
36
+ if (!options.name && !options.className && !options.tag) {
37
+ throw new Error('Give a name, --class or --tag to search for');
38
+ }
39
+
40
+ const result = await session.findInstancesAsync({
41
+ root: options.root,
42
+ name: options.name,
43
+ exactName: options.exact,
44
+ className: options.className,
45
+ tag: options.tag,
46
+ limit: options.limit,
47
+ });
48
+
49
+ return {
50
+ ...result,
51
+ summary: result.truncated
52
+ ? `First ${result.instances.length} matches; raise --limit to see more`
53
+ : `${result.instances.length} match(es) among ${result.searched} instances`,
54
+ };
55
+ }
56
+
57
+ const columns: TableColumn<FoundInstance>[] = [
58
+ {
59
+ header: 'Path',
60
+ value: (i) => i.path,
61
+ format: (v) => OutputHelper.formatHint(v),
62
+ },
63
+ { header: 'Class', value: (i) => i.className },
64
+ ];
65
+
66
+ export function formatFindText(result: FindResult): string {
67
+ if (result.instances.length === 0) return 'No instances match';
68
+ return `${formatTable(result.instances, columns)}\n${OutputHelper.formatDim(
69
+ result.summary
70
+ )}`;
71
+ }
72
+
73
+ interface FindArgs extends Omit<FindOptions, 'className'> {
74
+ class?: string;
75
+ }
76
+
77
+ export const findCommand = defineCommand<FindArgs, FindResult>({
78
+ group: 'explorer',
79
+ name: 'find',
80
+ description: 'Find instances by name, class or tag',
81
+ category: 'execution',
82
+ safety: 'read',
83
+ scope: 'session',
84
+ args: {
85
+ name: arg.positional({
86
+ description: 'Text in the instance name (case-insensitive)',
87
+ required: false,
88
+ }),
89
+ exact: arg.flag({
90
+ description: 'Match the whole name instead of part of it',
91
+ }),
92
+ class: arg.option({
93
+ description: 'Only instances of this class or a subclass (IsA)',
94
+ }),
95
+ tag: arg.option({
96
+ description: 'Only instances with this CollectionService tag',
97
+ }),
98
+ root: arg.option({
99
+ description:
100
+ 'Dot-separated path to search under (default: the whole DataModel)',
101
+ }),
102
+ limit: arg.option({
103
+ description: 'Stop after this many matches (default: 200)',
104
+ type: 'number',
105
+ alias: 'n',
106
+ }),
107
+ },
108
+ cli: {
109
+ format: formatFindText,
110
+ },
111
+ handler: async (session, { class: className, ...args }) =>
112
+ findInstancesHandlerAsync(session, { ...args, className }),
113
+ });
@@ -0,0 +1,214 @@
1
+ --!strict
2
+ --[=[
3
+ Action that searches the source of every script under `root` for `pattern`,
4
+ answering with each matching line.
5
+
6
+ - Matches the text plainly and case-insensitively by default. `luaPattern`
7
+ treats `pattern` as a Lua pattern, and `caseSensitive` keeps case.
8
+ - Reads a script's open editor text when it has one, so unsaved edits are
9
+ searched as they read in Studio.
10
+ - Stops after `limit` matching lines.
11
+
12
+ Request: `{ type: "grepScripts", payload: { pattern, root?, luaPattern?, caseSensitive?, limit? } }`
13
+ Response: `{ type: "grepScriptsResult", payload: { matches, searched, truncated } }`
14
+
15
+ @class GrepScriptsAction
16
+ ]=]
17
+
18
+ local DEFAULT_LIMIT = 200
19
+ local MAX_LINE_LENGTH = 300
20
+ local YIELD_EVERY = 200
21
+ -- Studio's own instances, which the place does not contain
22
+ local ENGINE_SERVICES = table.freeze({
23
+ CoreGui = true,
24
+ CorePackages = true,
25
+ RobloxReplicatedStorage = true,
26
+ })
27
+
28
+ local GrepScriptsAction = {}
29
+
30
+ type GrepMatch = {
31
+ path: string,
32
+ className: string,
33
+ line: number,
34
+ text: string,
35
+ }
36
+
37
+ --[=[
38
+ Registers the `grepScripts` handler.
39
+ ]=]
40
+ function GrepScriptsAction.register(router: any)
41
+ router:setResponseType("grepScripts", "grepScriptsResult")
42
+ router:register(
43
+ "grepScripts",
44
+ function(payload: { [string]: any }, _requestId: string, _sessionId: string): { [string]: any }?
45
+ local pattern = payload.pattern
46
+ if type(pattern) ~= "string" or pattern == "" then
47
+ return {
48
+ success = false,
49
+ code = "INVALID_PAYLOAD",
50
+ message = "grepScripts needs a pattern",
51
+ }
52
+ end
53
+
54
+ local luaPattern = payload.luaPattern == true
55
+ local caseSensitive = payload.caseSensitive == true
56
+ if luaPattern then
57
+ local ok, err = pcall(string.find, "", pattern)
58
+ if not ok then
59
+ return {
60
+ success = false,
61
+ code = "INVALID_PAYLOAD",
62
+ message = `Invalid Lua pattern: {err}`,
63
+ }
64
+ end
65
+ end
66
+
67
+ local root: Instance? = game
68
+ if type(payload.root) == "string" then
69
+ root = GrepScriptsAction._resolveInstance(payload.root)
70
+ if not root then
71
+ return {
72
+ success = false,
73
+ code = "INSTANCE_NOT_FOUND",
74
+ message = `Instance not found at path: {payload.root}`,
75
+ }
76
+ end
77
+ end
78
+
79
+ local needle = if caseSensitive
80
+ then pattern
81
+ elseif luaPattern then GrepScriptsAction._lowerPattern(pattern)
82
+ else string.lower(pattern)
83
+ local limit = if type(payload.limit) == "number" then payload.limit else DEFAULT_LIMIT
84
+
85
+ local matches: { GrepMatch } = {}
86
+ local searched = 0
87
+ local truncated = false
88
+ for _, script in GrepScriptsAction._getScripts(root :: Instance) do
89
+ searched += 1
90
+ if searched % YIELD_EVERY == 0 then
91
+ task.wait()
92
+ end
93
+
94
+ local source = GrepScriptsAction._readSource(script)
95
+ for lineNumber, rawText in string.split(source, "\n") do
96
+ local text = string.gsub(rawText, "\r$", "")
97
+ local haystack = if caseSensitive then text else string.lower(text)
98
+ if string.find(haystack, needle, 1, not luaPattern) then
99
+ if #matches >= limit then
100
+ truncated = true
101
+ break
102
+ end
103
+ table.insert(matches, {
104
+ path = script:GetFullName(),
105
+ className = script.ClassName,
106
+ line = lineNumber,
107
+ text = string.sub(text, 1, MAX_LINE_LENGTH),
108
+ })
109
+ end
110
+ end
111
+
112
+ if truncated then
113
+ break
114
+ end
115
+ end
116
+
117
+ return {
118
+ matches = matches,
119
+ searched = searched,
120
+ truncated = truncated,
121
+ }
122
+ end
123
+ )
124
+ end
125
+
126
+ --[[
127
+ Lowercases a Lua pattern's literal letters, leaving each character after a
128
+ `%` alone, since `%S` and `%s` are opposite classes.
129
+ ]]
130
+ function GrepScriptsAction._lowerPattern(pattern: string): string
131
+ local lowered = {}
132
+ local isEscaped = false
133
+ for _, char in string.split(pattern, "") do
134
+ table.insert(lowered, if isEscaped then char else string.lower(char))
135
+ isEscaped = not isEscaped and char == "%"
136
+ end
137
+ return table.concat(lowered)
138
+ end
139
+
140
+ function GrepScriptsAction._readSource(script: LuaSourceContainer): string
141
+ local editorOk, editorSource = pcall(function()
142
+ return game:GetService("ScriptEditorService"):GetEditorSource(script)
143
+ end)
144
+ if editorOk and type(editorSource) == "string" then
145
+ return editorSource
146
+ end
147
+
148
+ local ok, source = pcall(function()
149
+ return (script :: any).Source
150
+ end)
151
+ return if ok and type(source) == "string" then source else ""
152
+ end
153
+
154
+ --[[
155
+ Returns the scripts under `root`. For game, that is those of each place
156
+ service the plugin may read, since some refuse access.
157
+ ]]
158
+ function GrepScriptsAction._getScripts(root: Instance): { LuaSourceContainer }
159
+ local roots = if root == game then game:GetChildren() else { root }
160
+
161
+ local scripts = {}
162
+ for _, searchRoot in roots do
163
+ if root == game and ENGINE_SERVICES[searchRoot.ClassName] then
164
+ continue
165
+ end
166
+ local ok, descendants = pcall(searchRoot.GetDescendants, searchRoot)
167
+ if not ok then
168
+ continue
169
+ end
170
+ if searchRoot:IsA("LuaSourceContainer") then
171
+ table.insert(scripts, searchRoot)
172
+ end
173
+ for _, descendant in descendants do
174
+ if descendant:IsA("LuaSourceContainer") then
175
+ table.insert(scripts, descendant)
176
+ end
177
+ end
178
+ end
179
+ return scripts
180
+ end
181
+
182
+ --[[
183
+ Walks `path` down from game, falling back to GetService for top-level names
184
+ that are not children by that name. A leading "game" is optional.
185
+ ]]
186
+ function GrepScriptsAction._resolveInstance(path: string): Instance?
187
+ local parts = string.split(path, ".")
188
+ local startIndex = if parts[1] == "game" then 2 else 1
189
+
190
+ local current: Instance = game
191
+ for index = startIndex, #parts do
192
+ local part = parts[index]
193
+
194
+ local ok, child = pcall(current.FindFirstChild, current, part)
195
+ if ok and child then
196
+ current = child
197
+ continue
198
+ end
199
+
200
+ if current ~= game then
201
+ return nil
202
+ end
203
+
204
+ local serviceOk, service = pcall(game.GetService, game, part)
205
+ if not (serviceOk and service) then
206
+ return nil
207
+ end
208
+ current = service
209
+ end
210
+
211
+ return current
212
+ end
213
+
214
+ return GrepScriptsAction