@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,622 +0,0 @@
1
- --[[
2
- Tests for DiscoveryStateMachine.
3
-
4
- Covers all state transitions, port scanning, callback invocations,
5
- and disconnect recovery using mock callbacks.
6
-
7
- Since the state machine uses os.clock() for timing, tests that need
8
- to control timing set _nextPollAt directly on the instance.
9
- ]]
10
-
11
- local DiscoveryStateMachine = require("../../studio-bridge-plugin/src/Shared/DiscoveryStateMachine")
12
-
13
- -- ---------------------------------------------------------------------------
14
- -- Test helpers
15
- -- ---------------------------------------------------------------------------
16
-
17
- local function assertEqual(actual: any, expected: any, label: string?)
18
- if actual ~= expected then
19
- error(
20
- string.format(
21
- "%sexpected %s, got %s",
22
- if label then label .. ": " else "",
23
- tostring(expected),
24
- tostring(actual)
25
- )
26
- )
27
- end
28
- end
29
-
30
- local function assertTrue(value: any, label: string?)
31
- if not value then
32
- error(string.format("%sexpected truthy value, got %s", if label then label .. ": " else "", tostring(value)))
33
- end
34
- end
35
-
36
- -- ---------------------------------------------------------------------------
37
- -- Mock callback factory
38
- -- ---------------------------------------------------------------------------
39
-
40
- local function createMockCallbacks()
41
- local mock = {
42
- stateChanges = {} :: { { old: string, new: string } },
43
- connectedCalls = {} :: { any },
44
- disconnectedCalls = {} :: { string? },
45
- scanPortsCalls = {} :: { { number } },
46
- connectWebSocketCalls = {} :: { string },
47
- -- Which ports respond successfully (by port number)
48
- respondingPorts = {} :: { [number]: string },
49
- -- Default: no ports respond
50
- defaultResponse = nil :: string?,
51
- wsResult = { success = false, connection = nil :: any? },
52
- }
53
-
54
- -- Mock scanPortsAsync: checks respondingPorts, returns first match
55
- mock.scanPortsAsync = function(ports: { number }, _timeoutSec: number): (number?, string?)
56
- table.insert(mock.scanPortsCalls, ports)
57
- for _, port in ports do
58
- if mock.respondingPorts[port] then
59
- return port, mock.respondingPorts[port]
60
- end
61
- end
62
- if mock.defaultResponse then
63
- return ports[1], mock.defaultResponse
64
- end
65
- return nil, nil
66
- end
67
-
68
- mock.connectWebSocket = function(url: string): (boolean, any?)
69
- table.insert(mock.connectWebSocketCalls, url)
70
- return mock.wsResult.success, mock.wsResult.connection
71
- end
72
-
73
- mock.onStateChange = function(oldState: string, newState: string)
74
- table.insert(mock.stateChanges, { old = oldState, new = newState })
75
- end
76
-
77
- mock.onConnected = function(connection: any, _port: number)
78
- table.insert(mock.connectedCalls, connection)
79
- end
80
-
81
- mock.onDisconnected = function(reason: string?)
82
- table.insert(mock.disconnectedCalls, reason)
83
- end
84
-
85
- return mock
86
- end
87
-
88
- -- Small config for fast tests
89
- local TEST_CONFIG = {
90
- portRange = { min = 38740, max = 38745 },
91
- pollIntervalSec = 2,
92
- }
93
-
94
- -- ---------------------------------------------------------------------------
95
- -- Tests
96
- -- ---------------------------------------------------------------------------
97
-
98
- local tests = {}
99
-
100
- -- 1. State starts as idle
101
- table.insert(tests, {
102
- name = "state starts as idle",
103
- fn = function()
104
- local mock = createMockCallbacks()
105
- local sm = DiscoveryStateMachine.new(TEST_CONFIG, mock)
106
- assertEqual(sm:getState(), "idle")
107
- end,
108
- })
109
-
110
- -- 2. start() transitions to searching
111
- table.insert(tests, {
112
- name = "start() transitions from idle to searching",
113
- fn = function()
114
- local mock = createMockCallbacks()
115
- local sm = DiscoveryStateMachine.new(TEST_CONFIG, mock)
116
- sm:start()
117
- assertEqual(sm:getState(), "searching")
118
- assertEqual(#mock.stateChanges, 1)
119
- assertEqual(mock.stateChanges[1].old, "idle")
120
- assertEqual(mock.stateChanges[1].new, "searching")
121
- end,
122
- })
123
-
124
- -- 3. Scan finds a port → triggers connecting
125
- table.insert(tests, {
126
- name = "scan success triggers transition to connecting",
127
- fn = function()
128
- local mock = createMockCallbacks()
129
- mock.defaultResponse = '{"status":"ok"}'
130
- mock.wsResult = { success = false, connection = nil }
131
- local sm = DiscoveryStateMachine.new(TEST_CONFIG, mock)
132
- sm:start()
133
- sm:pollAsync()
134
- local foundConnecting = false
135
- for _, change in mock.stateChanges do
136
- if change.old == "searching" and change.new == "connecting" then
137
- foundConnecting = true
138
- break
139
- end
140
- end
141
- assertTrue(foundConnecting, "should have transitioned to connecting")
142
- end,
143
- })
144
-
145
- -- 4. connectWebSocket success triggers connected + onConnected callback
146
- table.insert(tests, {
147
- name = "connectWebSocket success triggers connected and onConnected callback",
148
- fn = function()
149
- local mock = createMockCallbacks()
150
- mock.defaultResponse = '{"status":"ok"}'
151
- local fakeConnection = { id = "conn-42" }
152
- mock.wsResult = { success = true, connection = fakeConnection }
153
- local sm = DiscoveryStateMachine.new(TEST_CONFIG, mock)
154
- sm:start()
155
- sm:pollAsync()
156
- assertEqual(sm:getState(), "connected")
157
- assertEqual(#mock.connectedCalls, 1)
158
- assertEqual(mock.connectedCalls[1].id, "conn-42")
159
- assertEqual(mock.stateChanges[1].new, "searching")
160
- assertEqual(mock.stateChanges[2].new, "connecting")
161
- assertEqual(mock.stateChanges[3].new, "connected")
162
- end,
163
- })
164
-
165
- -- 5. onDisconnect() while connected transitions to searching
166
- table.insert(tests, {
167
- name = "onDisconnect() while connected transitions to searching",
168
- fn = function()
169
- local mock = createMockCallbacks()
170
- mock.defaultResponse = '{"status":"ok"}'
171
- mock.wsResult = { success = true, connection = { id = "ws-1" } }
172
- local sm = DiscoveryStateMachine.new(TEST_CONFIG, mock)
173
- sm:start()
174
- sm:pollAsync()
175
- assertEqual(sm:getState(), "connected")
176
-
177
- sm:onDisconnect("server closed")
178
- assertEqual(sm:getState(), "searching")
179
- assertEqual(#mock.disconnectedCalls, 1)
180
- assertEqual(mock.disconnectedCalls[1], "server closed")
181
- end,
182
- })
183
-
184
- -- 6. After disconnect, immediate scan on next pollAsync
185
- table.insert(tests, {
186
- name = "disconnect triggers immediate scan on next pollAsync",
187
- fn = function()
188
- local mock = createMockCallbacks()
189
- mock.defaultResponse = '{"status":"ok"}'
190
- mock.wsResult = { success = true, connection = { id = "ws-1" } }
191
- local sm = DiscoveryStateMachine.new(TEST_CONFIG, mock)
192
- sm:start()
193
- sm:pollAsync()
194
- assertEqual(sm:getState(), "connected", "initial connection")
195
-
196
- sm:onDisconnect("lost")
197
- assertEqual(sm:getState(), "searching", "searching immediately after disconnect")
198
-
199
- -- Next pollAsync should scan immediately (nextPollAt was set to 0)
200
- sm:pollAsync()
201
- assertEqual(sm:getState(), "connected", "reconnected on next poll")
202
- assertEqual(#mock.connectedCalls, 2, "onConnected called twice")
203
- end,
204
- })
205
-
206
- -- 7. stop() from any state transitions to idle
207
- table.insert(tests, {
208
- name = "stop() from any state transitions to idle",
209
- fn = function()
210
- local mock = createMockCallbacks()
211
- mock.defaultResponse = '{"status":"ok"}'
212
- mock.wsResult = { success = true, connection = { id = "ws-1" } }
213
-
214
- -- stop from searching
215
- local sm1 = DiscoveryStateMachine.new(TEST_CONFIG, mock)
216
- sm1:start()
217
- assertEqual(sm1:getState(), "searching")
218
- sm1:stop()
219
- assertEqual(sm1:getState(), "idle", "stop from searching")
220
-
221
- -- stop from connected
222
- local sm2 = DiscoveryStateMachine.new(TEST_CONFIG, mock)
223
- sm2:start()
224
- sm2:pollAsync()
225
- assertEqual(sm2:getState(), "connected")
226
- sm2:stop()
227
- assertEqual(sm2:getState(), "idle", "stop from connected")
228
- end,
229
- })
230
-
231
- -- 8. Disconnect primes immediate scan
232
- table.insert(tests, {
233
- name = "disconnect skips backoff and primes immediate scan",
234
- fn = function()
235
- local mock = createMockCallbacks()
236
- mock.defaultResponse = '{"status":"ok"}'
237
- mock.wsResult = { success = true, connection = { id = "ws-1" } }
238
- local sm = DiscoveryStateMachine.new(TEST_CONFIG, mock)
239
- sm:start()
240
- sm:pollAsync()
241
- assertEqual(sm:getState(), "connected")
242
-
243
- -- After disconnect, nextPollAt should be 0 (immediate)
244
- mock.defaultResponse = nil
245
- sm:onDisconnect("lost")
246
- assertEqual(sm:getState(), "searching", "searching immediately")
247
-
248
- local scanCallsBefore = #mock.scanPortsCalls
249
- sm:pollAsync()
250
- assertTrue(#mock.scanPortsCalls > scanCallsBefore, "scan triggered on first poll after disconnect")
251
- end,
252
- })
253
-
254
- -- 9. Port scanning passes correct port list
255
- table.insert(tests, {
256
- name = "port scanning passes correct port list to scanPortsAsync",
257
- fn = function()
258
- local config = {
259
- portRange = { min = 38740, max = 38742 },
260
- pollIntervalSec = 0.1,
261
- }
262
- local mock = createMockCallbacks()
263
- mock.respondingPorts[38742] = '{"status":"ok"}'
264
- mock.wsResult = { success = true, connection = { id = "ws-1" } }
265
- local sm = DiscoveryStateMachine.new(config, mock)
266
- sm:start()
267
- sm:pollAsync()
268
- assertEqual(sm:getState(), "connected")
269
-
270
- -- Verify the ports passed to scanPortsAsync
271
- assertTrue(#mock.scanPortsCalls >= 1, "scanPortsAsync should have been called")
272
- local ports = mock.scanPortsCalls[1]
273
- assertEqual(#ports, 3, "should receive 3 ports")
274
- assertEqual(ports[1], 38740)
275
- assertEqual(ports[2], 38741)
276
- assertEqual(ports[3], 38742)
277
- end,
278
- })
279
-
280
- -- 10. State change callback fires on each transition
281
- table.insert(tests, {
282
- name = "state change callback fires on each transition",
283
- fn = function()
284
- local mock = createMockCallbacks()
285
- mock.defaultResponse = '{"status":"ok"}'
286
- mock.wsResult = { success = true, connection = { id = "ws-1" } }
287
- local sm = DiscoveryStateMachine.new(TEST_CONFIG, mock)
288
-
289
- sm:start() -- idle -> searching
290
- sm:pollAsync() -- searching -> connecting -> connected
291
-
292
- sm:onDisconnect("lost") -- connected -> searching (immediate)
293
-
294
- assertEqual(#mock.stateChanges, 4)
295
- assertEqual(mock.stateChanges[1].old, "idle")
296
- assertEqual(mock.stateChanges[1].new, "searching")
297
- assertEqual(mock.stateChanges[2].old, "searching")
298
- assertEqual(mock.stateChanges[2].new, "connecting")
299
- assertEqual(mock.stateChanges[3].old, "connecting")
300
- assertEqual(mock.stateChanges[3].new, "connected")
301
- assertEqual(mock.stateChanges[4].old, "connected")
302
- assertEqual(mock.stateChanges[4].new, "searching")
303
- end,
304
- })
305
-
306
- -- 11. scanPortsAsync receives all ports including the one that responds
307
- table.insert(tests, {
308
- name = "scanPortsAsync receives all ports in range",
309
- fn = function()
310
- local config = {
311
- portRange = { min = 38740, max = 38745 },
312
- pollIntervalSec = 0.1,
313
- }
314
- local mock = createMockCallbacks()
315
- mock.respondingPorts[38744] = '{"status":"ok"}'
316
- mock.wsResult = { success = true, connection = { id = "ws-1" } }
317
- local sm = DiscoveryStateMachine.new(config, mock)
318
- sm:start()
319
- sm:pollAsync()
320
- assertEqual(sm:getState(), "connected")
321
-
322
- local ports = mock.scanPortsCalls[1]
323
- assertEqual(#ports, 6, "should receive all 6 ports in range")
324
- end,
325
- })
326
-
327
- -- 12. First poll after start() scans immediately
328
- table.insert(tests, {
329
- name = "immediate port scan after start (no delay for first scan)",
330
- fn = function()
331
- local mock = createMockCallbacks()
332
- mock.defaultResponse = '{"status":"ok"}'
333
- mock.wsResult = { success = true, connection = { id = "ws-1" } }
334
- local sm = DiscoveryStateMachine.new(TEST_CONFIG, mock)
335
- sm:start()
336
- sm:pollAsync()
337
- assertEqual(sm:getState(), "connected", "should connect on first poll after start")
338
- end,
339
- })
340
-
341
- -- 13. pollAsync() is a no-op in idle state
342
- table.insert(tests, {
343
- name = "pollAsync() is a no-op in idle state",
344
- fn = function()
345
- local mock = createMockCallbacks()
346
- local sm = DiscoveryStateMachine.new(TEST_CONFIG, mock)
347
- sm:pollAsync()
348
- assertEqual(sm:getState(), "idle")
349
- assertEqual(#mock.scanPortsCalls, 0)
350
- assertEqual(#mock.stateChanges, 0)
351
- end,
352
- })
353
-
354
- -- 14. pollAsync() is a no-op in connected state
355
- table.insert(tests, {
356
- name = "pollAsync() is a no-op in connected state",
357
- fn = function()
358
- local mock = createMockCallbacks()
359
- mock.defaultResponse = '{"status":"ok"}'
360
- mock.wsResult = { success = true, connection = { id = "ws-1" } }
361
- local sm = DiscoveryStateMachine.new(TEST_CONFIG, mock)
362
- sm:start()
363
- sm:pollAsync()
364
- assertEqual(sm:getState(), "connected")
365
-
366
- local callsBefore = #mock.scanPortsCalls
367
- sm:pollAsync()
368
- assertEqual(sm:getState(), "connected")
369
- assertEqual(#mock.scanPortsCalls, callsBefore, "no new scans in connected state")
370
- end,
371
- })
372
-
373
- -- 15. onDisconnect() is a no-op when not connected
374
- table.insert(tests, {
375
- name = "onDisconnect() is a no-op when not connected",
376
- fn = function()
377
- local mock = createMockCallbacks()
378
- local sm = DiscoveryStateMachine.new(TEST_CONFIG, mock)
379
-
380
- sm:onDisconnect("test")
381
- assertEqual(sm:getState(), "idle")
382
- assertEqual(#mock.disconnectedCalls, 0)
383
-
384
- sm:start()
385
- sm:onDisconnect("test")
386
- assertEqual(sm:getState(), "searching")
387
- assertEqual(#mock.disconnectedCalls, 0)
388
- end,
389
- })
390
-
391
- -- 16. start() is a no-op when not idle
392
- table.insert(tests, {
393
- name = "start() is a no-op when not in idle state",
394
- fn = function()
395
- local mock = createMockCallbacks()
396
- local sm = DiscoveryStateMachine.new(TEST_CONFIG, mock)
397
- sm:start()
398
- local changesBefore = #mock.stateChanges
399
- sm:start()
400
- assertEqual(#mock.stateChanges, changesBefore)
401
- assertEqual(sm:getState(), "searching")
402
- end,
403
- })
404
-
405
- -- 17. Connecting fails then goes back to searching
406
- table.insert(tests, {
407
- name = "connectWebSocket failure returns to searching",
408
- fn = function()
409
- local mock = createMockCallbacks()
410
- mock.defaultResponse = '{"status":"ok"}'
411
- mock.wsResult = { success = false, connection = nil }
412
- local sm = DiscoveryStateMachine.new(TEST_CONFIG, mock)
413
- sm:start()
414
- sm:pollAsync()
415
- assertEqual(sm:getState(), "searching")
416
- local found = false
417
- for _, change in mock.stateChanges do
418
- if change.old == "connecting" and change.new == "searching" then
419
- found = true
420
- break
421
- end
422
- end
423
- assertTrue(found, "should transition from connecting back to searching")
424
- end,
425
- })
426
-
427
- -- 18. stop() from connected fires onDisconnected
428
- table.insert(tests, {
429
- name = "stop() from connected fires onDisconnected callback",
430
- fn = function()
431
- local mock = createMockCallbacks()
432
- mock.defaultResponse = '{"status":"ok"}'
433
- mock.wsResult = { success = true, connection = { id = "ws-1" } }
434
- local sm = DiscoveryStateMachine.new(TEST_CONFIG, mock)
435
- sm:start()
436
- sm:pollAsync()
437
- assertEqual(sm:getState(), "connected")
438
-
439
- sm:stop()
440
- assertEqual(sm:getState(), "idle")
441
- assertEqual(#mock.disconnectedCalls, 1)
442
- assertEqual(mock.disconnectedCalls[1], "stopped")
443
- end,
444
- })
445
-
446
- -- 19. Uses default config when none provided
447
- table.insert(tests, {
448
- name = "uses default config when none provided",
449
- fn = function()
450
- local mock = createMockCallbacks()
451
- mock.defaultResponse = '{"status":"ok"}'
452
- mock.wsResult = { success = true, connection = { id = "ws-1" } }
453
- local sm = DiscoveryStateMachine.new(nil, mock)
454
- sm:start()
455
- sm:pollAsync()
456
- assertEqual(sm:getState(), "connected")
457
- end,
458
- })
459
-
460
- -- 20. WebSocket URL format is correct
461
- table.insert(tests, {
462
- name = "WebSocket URL format is ws://localhost:{port}/plugin",
463
- fn = function()
464
- local config = {
465
- portRange = { min = 38741, max = 38741 },
466
- pollIntervalSec = 0.1,
467
- }
468
- local mock = createMockCallbacks()
469
- mock.defaultResponse = '{"status":"ok"}'
470
- mock.wsResult = { success = false, connection = nil }
471
- local sm = DiscoveryStateMachine.new(config, mock)
472
- sm:start()
473
- sm:pollAsync()
474
- assertTrue(#mock.connectWebSocketCalls >= 1, "should have tried to connect")
475
- assertEqual(mock.connectWebSocketCalls[1], "ws://localhost:38741/plugin")
476
- end,
477
- })
478
-
479
- -- 21. pollAsync respects nextPollAt timing (doesn't scan when not due)
480
- table.insert(tests, {
481
- name = "pollAsync skips scan when nextPollAt is in the future",
482
- fn = function()
483
- local mock = createMockCallbacks()
484
- local sm = DiscoveryStateMachine.new(TEST_CONFIG, mock)
485
- sm:start()
486
- -- First poll scans (nextPollAt was 0)
487
- sm:pollAsync()
488
- local callsAfterFirst = #mock.scanPortsCalls
489
- assertTrue(callsAfterFirst > 0, "first poll should scan")
490
-
491
- -- Next poll should NOT scan (nextPollAt is in the future)
492
- sm:pollAsync()
493
- assertEqual(#mock.scanPortsCalls, callsAfterFirst, "second poll should not scan yet")
494
- end,
495
- })
496
-
497
- -- 22. onDisconnect from connected transitions to searching with immediate scan
498
- table.insert(tests, {
499
- name = "onDisconnect from connected transitions to searching with primed scan",
500
- fn = function()
501
- local mock = createMockCallbacks()
502
- mock.defaultResponse = '{"status":"ok"}'
503
- mock.wsResult = { success = true, connection = { id = "ws-1" } }
504
- local sm = DiscoveryStateMachine.new(TEST_CONFIG, mock)
505
- sm:start()
506
- sm:pollAsync()
507
- assertEqual(sm:getState(), "connected", "initial connection")
508
-
509
- sm:onDisconnect("error: connection lost")
510
- assertEqual(sm:getState(), "searching", "state after disconnect")
511
- assertEqual(#mock.disconnectedCalls, 1, "onDisconnected called once")
512
- assertEqual(mock.disconnectedCalls[1], "error: connection lost", "disconnect reason")
513
-
514
- local scanCallsBefore = #mock.scanPortsCalls
515
- sm:pollAsync()
516
- assertTrue(#mock.scanPortsCalls > scanCallsBefore, "scan triggered immediately after disconnect")
517
- end,
518
- })
519
-
520
- -- 23. Full disconnect + reconnect round-trip
521
- table.insert(tests, {
522
- name = "disconnect then immediate re-discovery and reconnection",
523
- fn = function()
524
- local mock = createMockCallbacks()
525
- mock.defaultResponse = '{"status":"ok"}'
526
- mock.wsResult = { success = true, connection = { id = "ws-1" } }
527
- local sm = DiscoveryStateMachine.new(TEST_CONFIG, mock)
528
- sm:start()
529
- sm:pollAsync()
530
- assertEqual(sm:getState(), "connected", "initial connection")
531
- assertEqual(#mock.connectedCalls, 1)
532
-
533
- sm:onDisconnect("lost")
534
- sm:pollAsync()
535
- assertEqual(sm:getState(), "connected", "reconnected after re-discovery")
536
- assertEqual(#mock.connectedCalls, 2, "onConnected called again")
537
- end,
538
- })
539
-
540
- -- 24. onDisconnect is no-op when not in connected state
541
- table.insert(tests, {
542
- name = "onDisconnect is no-op when not in connected state",
543
- fn = function()
544
- local mock = createMockCallbacks()
545
- local sm = DiscoveryStateMachine.new(TEST_CONFIG, mock)
546
-
547
- sm:onDisconnect("some reason")
548
- assertEqual(sm:getState(), "idle", "idle unchanged")
549
- assertEqual(#mock.disconnectedCalls, 0)
550
-
551
- sm:start()
552
- sm:onDisconnect("some reason")
553
- assertEqual(sm:getState(), "searching", "searching unchanged")
554
- assertEqual(#mock.disconnectedCalls, 0)
555
- end,
556
- })
557
-
558
- -- 25. scanPortsAsync callback is used when provided
559
- table.insert(tests, {
560
- name = "scanPortsAsync callback is used when provided",
561
- fn = function()
562
- local config = {
563
- portRange = { min = 38741, max = 38744 },
564
- pollIntervalSec = 0.1,
565
- }
566
- local mock = createMockCallbacks()
567
- local scanPortsCalled = false
568
- local scannedPorts: { number } = {}
569
-
570
- mock.scanPortsAsync = function(ports: { number }): (number?, string?)
571
- scanPortsCalled = true
572
- scannedPorts = ports
573
- return 38743, '{"status":"ok"}'
574
- end
575
-
576
- mock.wsResult = { success = true, connection = { id = "ws-scan" } }
577
- local sm = DiscoveryStateMachine.new(config, mock)
578
- sm:start()
579
- sm:pollAsync()
580
-
581
- assertTrue(scanPortsCalled, "scanPortsAsync should have been called")
582
- assertEqual(#scannedPorts, 4, "should receive all 4 ports")
583
- assertEqual(sm:getState(), "connected", "should connect via scanPortsAsync result")
584
- assertEqual(#mock.scanPortsCalls, 0, "default scanPortsAsync not called when overridden")
585
- end,
586
- })
587
-
588
- -- 26. scanPortsAsync returns nil when no port found
589
- table.insert(tests, {
590
- name = "scanPortsAsync returning nil stays in searching",
591
- fn = function()
592
- local mock = createMockCallbacks()
593
-
594
- mock.scanPortsAsync = function(_ports: { number }): (number?, string?)
595
- return nil, nil
596
- end
597
-
598
- local sm = DiscoveryStateMachine.new(TEST_CONFIG, mock)
599
- sm:start()
600
- sm:pollAsync()
601
- assertEqual(sm:getState(), "searching", "stays searching when scanPortsAsync finds nothing")
602
- end,
603
- })
604
-
605
- -- 27. stop() from connected fires onDisconnected callback
606
- table.insert(tests, {
607
- name = "stop() from connected fires onDisconnected",
608
- fn = function()
609
- local mock = createMockCallbacks()
610
- mock.defaultResponse = '{"status":"ok"}'
611
- mock.wsResult = { success = true, connection = { id = "ws-1" } }
612
- local sm = DiscoveryStateMachine.new(TEST_CONFIG, mock)
613
- sm:start()
614
- sm:pollAsync()
615
- sm:stop()
616
- assertEqual(sm:getState(), "idle")
617
- assertTrue(#mock.disconnectedCalls >= 1, "onDisconnected called")
618
- assertEqual(mock.disconnectedCalls[#mock.disconnectedCalls], "stopped")
619
- end,
620
- })
621
-
622
- return tests