@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.
- package/CHANGELOG.md +10 -0
- package/README.md +109 -17
- package/build-scripts/transform-add-session-attribute.lua +1 -0
- package/deploy.nevermore.json +10 -0
- package/dist/src/bridge/bridge-connection.d.ts +26 -2
- package/dist/src/bridge/bridge-connection.d.ts.map +1 -1
- package/dist/src/bridge/bridge-connection.js +159 -14
- package/dist/src/bridge/bridge-connection.js.map +1 -1
- package/dist/src/bridge/bridge-connection.test.js +81 -0
- package/dist/src/bridge/bridge-connection.test.js.map +1 -1
- package/dist/src/bridge/bridge-session.d.ts +35 -2
- package/dist/src/bridge/bridge-session.d.ts.map +1 -1
- package/dist/src/bridge/bridge-session.js +185 -28
- package/dist/src/bridge/bridge-session.js.map +1 -1
- package/dist/src/bridge/bridge-session.test.js +6 -4
- package/dist/src/bridge/bridge-session.test.js.map +1 -1
- package/dist/src/bridge/index.d.ts +1 -1
- package/dist/src/bridge/index.d.ts.map +1 -1
- package/dist/src/bridge/index.js.map +1 -1
- package/dist/src/bridge/internal/bridge-client.d.ts +4 -0
- package/dist/src/bridge/internal/bridge-client.d.ts.map +1 -1
- package/dist/src/bridge/internal/bridge-client.js +20 -7
- package/dist/src/bridge/internal/bridge-client.js.map +1 -1
- package/dist/src/bridge/internal/bridge-client.test.js +18 -0
- package/dist/src/bridge/internal/bridge-client.test.js.map +1 -1
- package/dist/src/bridge/internal/bridge-host.d.ts +13 -1
- package/dist/src/bridge/internal/bridge-host.d.ts.map +1 -1
- package/dist/src/bridge/internal/bridge-host.js +35 -0
- package/dist/src/bridge/internal/bridge-host.js.map +1 -1
- package/dist/src/bridge/internal/bridge-host.test.js +31 -0
- package/dist/src/bridge/internal/bridge-host.test.js.map +1 -1
- package/dist/src/bridge/internal/hand-off.d.ts.map +1 -1
- package/dist/src/bridge/internal/hand-off.js +4 -1
- package/dist/src/bridge/internal/hand-off.js.map +1 -1
- package/dist/src/bridge/internal/host-protocol.d.ts +8 -0
- package/dist/src/bridge/internal/host-protocol.d.ts.map +1 -1
- package/dist/src/bridge/internal/host-protocol.js +120 -13
- package/dist/src/bridge/internal/host-protocol.js.map +1 -1
- package/dist/src/bridge/internal/host-protocol.test.js +13 -0
- package/dist/src/bridge/internal/host-protocol.test.js.map +1 -1
- package/dist/src/bridge/internal/instance-state.d.ts +9 -0
- package/dist/src/bridge/internal/instance-state.d.ts.map +1 -0
- package/dist/src/bridge/internal/instance-state.js +28 -0
- package/dist/src/bridge/internal/instance-state.js.map +1 -0
- package/dist/src/bridge/internal/transport-client.d.ts.map +1 -1
- package/dist/src/bridge/internal/transport-client.js +2 -1
- package/dist/src/bridge/internal/transport-client.js.map +1 -1
- package/dist/src/bridge/internal/transport-server.d.ts.map +1 -1
- package/dist/src/bridge/internal/transport-server.js +5 -1
- package/dist/src/bridge/internal/transport-server.js.map +1 -1
- package/dist/src/bridge/types.d.ts +43 -4
- package/dist/src/bridge/types.d.ts.map +1 -1
- package/dist/src/bridge/types.js.map +1 -1
- package/dist/src/cli/adapters/cli-command-adapter-handler.test.js +24 -0
- package/dist/src/cli/adapters/cli-command-adapter-handler.test.js.map +1 -1
- package/dist/src/cli/adapters/cli-command-adapter.d.ts +6 -0
- package/dist/src/cli/adapters/cli-command-adapter.d.ts.map +1 -1
- package/dist/src/cli/adapters/cli-command-adapter.js +107 -11
- package/dist/src/cli/adapters/cli-command-adapter.js.map +1 -1
- package/dist/src/cli/adapters/group-builder.d.ts +3 -0
- package/dist/src/cli/adapters/group-builder.d.ts.map +1 -1
- package/dist/src/cli/adapters/group-builder.js +34 -15
- package/dist/src/cli/adapters/group-builder.js.map +1 -1
- package/dist/src/cli/adapters/group-builder.test.js +26 -2
- package/dist/src/cli/adapters/group-builder.test.js.map +1 -1
- package/dist/src/cli/adapters/help-formatter.d.ts.map +1 -1
- package/dist/src/cli/adapters/help-formatter.js +4 -3
- package/dist/src/cli/adapters/help-formatter.js.map +1 -1
- package/dist/src/cli/adapters/help-formatter.test.js +1 -1
- package/dist/src/cli/adapters/help-formatter.test.js.map +1 -1
- package/dist/src/cli/adapters/playtest-edit-guard.test.d.ts +2 -0
- package/dist/src/cli/adapters/playtest-edit-guard.test.d.ts.map +1 -0
- package/dist/src/cli/adapters/playtest-edit-guard.test.js +28 -0
- package/dist/src/cli/adapters/playtest-edit-guard.test.js.map +1 -0
- package/dist/src/cli/args/global-args.d.ts +1 -1
- package/dist/src/cli/args/global-args.d.ts.map +1 -1
- package/dist/src/cli/cli-output-context.d.ts +14 -0
- package/dist/src/cli/cli-output-context.d.ts.map +1 -0
- package/dist/src/cli/cli-output-context.js +39 -0
- package/dist/src/cli/cli-output-context.js.map +1 -0
- package/dist/src/cli/cli-output-context.test.d.ts +2 -0
- package/dist/src/cli/cli-output-context.test.d.ts.map +1 -0
- package/dist/src/cli/cli-output-context.test.js +38 -0
- package/dist/src/cli/cli-output-context.test.js.map +1 -0
- package/dist/src/cli/cli.js +40 -17
- package/dist/src/cli/cli.js.map +1 -1
- package/dist/src/cli/managed-studio-run.d.ts +21 -0
- package/dist/src/cli/managed-studio-run.d.ts.map +1 -0
- package/dist/src/cli/managed-studio-run.js +75 -0
- package/dist/src/cli/managed-studio-run.js.map +1 -0
- package/dist/src/cli/managed-studio-run.test.d.ts +2 -0
- package/dist/src/cli/managed-studio-run.test.d.ts.map +1 -0
- package/dist/src/cli/managed-studio-run.test.js +52 -0
- package/dist/src/cli/managed-studio-run.test.js.map +1 -0
- package/dist/src/cli/resolve-session.js +1 -1
- package/dist/src/cli/resolve-session.js.map +1 -1
- package/dist/src/cli/resolve-session.test.js +1 -1
- package/dist/src/cli/resolve-session.test.js.map +1 -1
- package/dist/src/cli/script-executor.d.ts +13 -1
- package/dist/src/cli/script-executor.d.ts.map +1 -1
- package/dist/src/cli/script-executor.js +77 -47
- package/dist/src/cli/script-executor.js.map +1 -1
- package/dist/src/commands/console/exec/exec.d.ts +17 -0
- package/dist/src/commands/console/exec/exec.d.ts.map +1 -1
- package/dist/src/commands/console/exec/exec.js +39 -7
- package/dist/src/commands/console/exec/exec.js.map +1 -1
- package/dist/src/commands/console/exec/exec.test.js +62 -3
- package/dist/src/commands/console/exec/exec.test.js.map +1 -1
- package/dist/src/commands/console/logs/logs.d.ts +30 -2
- package/dist/src/commands/console/logs/logs.d.ts.map +1 -1
- package/dist/src/commands/console/logs/logs.js +82 -7
- package/dist/src/commands/console/logs/logs.js.map +1 -1
- package/dist/src/commands/console/logs/logs.test.js +63 -4
- package/dist/src/commands/console/logs/logs.test.js.map +1 -1
- package/dist/src/commands/explorer/export/export.d.ts +19 -0
- package/dist/src/commands/explorer/export/export.d.ts.map +1 -0
- package/dist/src/commands/explorer/export/export.js +37 -0
- package/dist/src/commands/explorer/export/export.js.map +1 -0
- package/dist/src/commands/explorer/export/export.test.d.ts +2 -0
- package/dist/src/commands/explorer/export/export.test.d.ts.map +1 -0
- package/dist/src/commands/explorer/export/export.test.js +25 -0
- package/dist/src/commands/explorer/export/export.test.js.map +1 -0
- package/dist/src/commands/explorer/find/find.d.ts +28 -0
- package/dist/src/commands/explorer/find/find.d.ts.map +1 -0
- package/dist/src/commands/explorer/find/find.js +75 -0
- package/dist/src/commands/explorer/find/find.js.map +1 -0
- package/dist/src/commands/explorer/find/find.test.d.ts +2 -0
- package/dist/src/commands/explorer/find/find.test.d.ts.map +1 -0
- package/dist/src/commands/explorer/find/find.test.js +44 -0
- package/dist/src/commands/explorer/find/find.test.js.map +1 -0
- package/dist/src/commands/explorer/grep/grep.d.ts +24 -0
- package/dist/src/commands/explorer/grep/grep.d.ts.map +1 -0
- package/dist/src/commands/explorer/grep/grep.js +63 -0
- package/dist/src/commands/explorer/grep/grep.js.map +1 -0
- package/dist/src/commands/explorer/grep/grep.test.d.ts +2 -0
- package/dist/src/commands/explorer/grep/grep.test.d.ts.map +1 -0
- package/dist/src/commands/explorer/grep/grep.test.js +42 -0
- package/dist/src/commands/explorer/grep/grep.test.js.map +1 -0
- package/dist/src/commands/explorer/import/import.d.ts +21 -0
- package/dist/src/commands/explorer/import/import.d.ts.map +1 -0
- package/dist/src/commands/explorer/import/import.js +37 -0
- package/dist/src/commands/explorer/import/import.js.map +1 -0
- package/dist/src/commands/explorer/import/import.test.d.ts +2 -0
- package/dist/src/commands/explorer/import/import.test.d.ts.map +1 -0
- package/dist/src/commands/explorer/import/import.test.js +45 -0
- package/dist/src/commands/explorer/import/import.test.js.map +1 -0
- package/dist/src/commands/formatters.test.js +7 -1
- package/dist/src/commands/formatters.test.js.map +1 -1
- package/dist/src/commands/framework/action-loader.d.ts +5 -1
- package/dist/src/commands/framework/action-loader.d.ts.map +1 -1
- package/dist/src/commands/framework/action-loader.js +9 -2
- package/dist/src/commands/framework/action-loader.js.map +1 -1
- package/dist/src/commands/framework/action-loader.test.js +23 -0
- package/dist/src/commands/framework/action-loader.test.js.map +1 -1
- package/dist/src/commands/framework/arg-builder.d.ts +15 -2
- package/dist/src/commands/framework/arg-builder.d.ts.map +1 -1
- package/dist/src/commands/framework/arg-builder.js +22 -2
- package/dist/src/commands/framework/arg-builder.js.map +1 -1
- package/dist/src/commands/framework/define-command.d.ts +19 -3
- package/dist/src/commands/framework/define-command.d.ts.map +1 -1
- package/dist/src/commands/framework/define-command.js.map +1 -1
- package/dist/src/commands/framework/index.d.ts +1 -1
- package/dist/src/commands/framework/index.d.ts.map +1 -1
- package/dist/src/commands/framework/index.js.map +1 -1
- package/dist/src/commands/index.d.ts +11 -0
- package/dist/src/commands/index.d.ts.map +1 -1
- package/dist/src/commands/index.js +11 -0
- package/dist/src/commands/index.js.map +1 -1
- package/dist/src/commands/linux/inject-credentials/inject-credentials.d.ts.map +1 -1
- package/dist/src/commands/linux/inject-credentials/inject-credentials.js +1 -3
- package/dist/src/commands/linux/inject-credentials/inject-credentials.js.map +1 -1
- package/dist/src/commands/linux/setup/setup.d.ts.map +1 -1
- package/dist/src/commands/linux/setup/setup.js +1 -3
- package/dist/src/commands/linux/setup/setup.js.map +1 -1
- package/dist/src/commands/linux/status/status.d.ts +9 -1
- package/dist/src/commands/linux/status/status.d.ts.map +1 -1
- package/dist/src/commands/linux/status/status.js +66 -86
- package/dist/src/commands/linux/status/status.js.map +1 -1
- package/dist/src/commands/playtest/player/add/add.d.ts +17 -0
- package/dist/src/commands/playtest/player/add/add.d.ts.map +1 -0
- package/dist/src/commands/playtest/player/add/add.js +45 -0
- package/dist/src/commands/playtest/player/add/add.js.map +1 -0
- package/dist/src/commands/playtest/player/list/list.d.ts +13 -0
- package/dist/src/commands/playtest/player/list/list.d.ts.map +1 -0
- package/dist/src/commands/playtest/player/list/list.js +33 -0
- package/dist/src/commands/playtest/player/list/list.js.map +1 -0
- package/dist/src/commands/playtest/player/playtest-players.d.ts +36 -0
- package/dist/src/commands/playtest/player/playtest-players.d.ts.map +1 -0
- package/dist/src/commands/playtest/player/playtest-players.js +187 -0
- package/dist/src/commands/playtest/player/playtest-players.js.map +1 -0
- package/dist/src/commands/playtest/player/playtest-players.test.d.ts +2 -0
- package/dist/src/commands/playtest/player/playtest-players.test.d.ts.map +1 -0
- package/dist/src/commands/playtest/player/playtest-players.test.js +89 -0
- package/dist/src/commands/playtest/player/playtest-players.test.js.map +1 -0
- package/dist/src/commands/playtest/player/remove/remove.d.ts +17 -0
- package/dist/src/commands/playtest/player/remove/remove.d.ts.map +1 -0
- package/dist/src/commands/playtest/player/remove/remove.js +42 -0
- package/dist/src/commands/playtest/player/remove/remove.js.map +1 -0
- package/dist/src/commands/playtest/playtest-sessions.d.ts +49 -0
- package/dist/src/commands/playtest/playtest-sessions.d.ts.map +1 -0
- package/dist/src/commands/playtest/playtest-sessions.js +283 -0
- package/dist/src/commands/playtest/playtest-sessions.js.map +1 -0
- package/dist/src/commands/playtest/playtest-sessions.test.d.ts +2 -0
- package/dist/src/commands/playtest/playtest-sessions.test.d.ts.map +1 -0
- package/dist/src/commands/playtest/playtest-sessions.test.js +87 -0
- package/dist/src/commands/playtest/playtest-sessions.test.js.map +1 -0
- package/dist/src/commands/playtest/restart/restart.d.ts +23 -0
- package/dist/src/commands/playtest/restart/restart.d.ts.map +1 -0
- package/dist/src/commands/playtest/restart/restart.js +105 -0
- package/dist/src/commands/playtest/restart/restart.js.map +1 -0
- package/dist/src/commands/playtest/run/run.d.ts +47 -0
- package/dist/src/commands/playtest/run/run.d.ts.map +1 -0
- package/dist/src/commands/playtest/run/run.js +137 -0
- package/dist/src/commands/playtest/run/run.js.map +1 -0
- package/dist/src/commands/playtest/start/start.d.ts +42 -0
- package/dist/src/commands/playtest/start/start.d.ts.map +1 -0
- package/dist/src/commands/playtest/start/start.js +70 -0
- package/dist/src/commands/playtest/start/start.js.map +1 -0
- package/dist/src/commands/playtest/stop/stop.d.ts +15 -0
- package/dist/src/commands/playtest/stop/stop.d.ts.map +1 -0
- package/dist/src/commands/playtest/stop/stop.js +24 -0
- package/dist/src/commands/playtest/stop/stop.js.map +1 -0
- package/dist/src/commands/process/close/close.d.ts +19 -10
- package/dist/src/commands/process/close/close.d.ts.map +1 -1
- package/dist/src/commands/process/close/close.js +118 -15
- package/dist/src/commands/process/close/close.js.map +1 -1
- package/dist/src/commands/process/close/close.test.d.ts +2 -0
- package/dist/src/commands/process/close/close.test.d.ts.map +1 -0
- package/dist/src/commands/process/close/close.test.js +190 -0
- package/dist/src/commands/process/close/close.test.js.map +1 -0
- package/dist/src/commands/process/info/info.d.ts +45 -5
- package/dist/src/commands/process/info/info.d.ts.map +1 -1
- package/dist/src/commands/process/info/info.js +117 -13
- package/dist/src/commands/process/info/info.js.map +1 -1
- package/dist/src/commands/process/info/info.test.js +140 -38
- package/dist/src/commands/process/info/info.test.js.map +1 -1
- package/dist/src/commands/process/launch/launch.d.ts +11 -1
- package/dist/src/commands/process/launch/launch.d.ts.map +1 -1
- package/dist/src/commands/process/launch/launch.js +37 -7
- package/dist/src/commands/process/launch/launch.js.map +1 -1
- package/dist/src/commands/process/launch/launch.test.js +99 -20
- package/dist/src/commands/process/launch/launch.test.js.map +1 -1
- package/dist/src/commands/process/list/list.d.ts +2 -1
- package/dist/src/commands/process/list/list.d.ts.map +1 -1
- package/dist/src/commands/process/list/list.js +9 -6
- package/dist/src/commands/process/list/list.js.map +1 -1
- package/dist/src/commands/process/list/list.test.js +1 -0
- package/dist/src/commands/process/list/list.test.js.map +1 -1
- package/dist/src/commands/process/run/run.d.ts +9 -0
- package/dist/src/commands/process/run/run.d.ts.map +1 -1
- package/dist/src/commands/process/run/run.js +15 -5
- package/dist/src/commands/process/run/run.js.map +1 -1
- package/dist/src/commands/serve/serve.d.ts +2 -0
- package/dist/src/commands/serve/serve.d.ts.map +1 -1
- package/dist/src/commands/serve/serve.js +17 -4
- package/dist/src/commands/serve/serve.js.map +1 -1
- package/dist/src/commands/serve/serve.test.js +9 -2
- package/dist/src/commands/serve/serve.test.js.map +1 -1
- package/dist/src/commands/viewport/screenshot/screenshot.d.ts.map +1 -1
- package/dist/src/commands/viewport/screenshot/screenshot.js +4 -1
- package/dist/src/commands/viewport/screenshot/screenshot.js.map +1 -1
- package/dist/src/commands/viewport/screenshot/screenshot.test.js +5 -9
- package/dist/src/commands/viewport/screenshot/screenshot.test.js.map +1 -1
- package/dist/src/deploy/deploy-places.d.ts +39 -0
- package/dist/src/deploy/deploy-places.d.ts.map +1 -0
- package/dist/src/deploy/deploy-places.js +162 -0
- package/dist/src/deploy/deploy-places.js.map +1 -0
- package/dist/src/deploy/deploy-places.test.d.ts +2 -0
- package/dist/src/deploy/deploy-places.test.d.ts.map +1 -0
- package/dist/src/deploy/deploy-places.test.js +96 -0
- package/dist/src/deploy/deploy-places.test.js.map +1 -0
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/plugin/persistent-plugin-installer.d.ts.map +1 -1
- package/dist/src/plugin/persistent-plugin-installer.js +2 -1
- package/dist/src/plugin/persistent-plugin-installer.js.map +1 -1
- package/dist/src/plugin/persistent-plugin-installer.test.js +3 -0
- package/dist/src/plugin/persistent-plugin-installer.test.js.map +1 -1
- package/dist/src/plugin/plugin-build-version.d.ts +7 -0
- package/dist/src/plugin/plugin-build-version.d.ts.map +1 -0
- package/dist/src/plugin/plugin-build-version.js +42 -0
- package/dist/src/plugin/plugin-build-version.js.map +1 -0
- package/dist/src/plugin/plugin-build-version.test.d.ts +2 -0
- package/dist/src/plugin/plugin-build-version.test.d.ts.map +1 -0
- package/dist/src/plugin/plugin-build-version.test.js +41 -0
- package/dist/src/plugin/plugin-build-version.test.js.map +1 -0
- package/dist/src/plugin/plugin-injector.d.ts.map +1 -1
- package/dist/src/plugin/plugin-injector.js +7 -1
- package/dist/src/plugin/plugin-injector.js.map +1 -1
- package/dist/src/process/studio-process-closer.d.ts +35 -0
- package/dist/src/process/studio-process-closer.d.ts.map +1 -0
- package/dist/src/process/studio-process-closer.js +129 -0
- package/dist/src/process/studio-process-closer.js.map +1 -0
- package/dist/src/process/studio-process-closer.test.d.ts +2 -0
- package/dist/src/process/studio-process-closer.test.d.ts.map +1 -0
- package/dist/src/process/studio-process-closer.test.js +80 -0
- package/dist/src/process/studio-process-closer.test.js.map +1 -0
- package/dist/src/process/studio-settings.d.ts +14 -0
- package/dist/src/process/studio-settings.d.ts.map +1 -0
- package/dist/src/process/studio-settings.js +50 -0
- package/dist/src/process/studio-settings.js.map +1 -0
- package/dist/src/process/studio-settings.test.d.ts +2 -0
- package/dist/src/process/studio-settings.test.d.ts.map +1 -0
- package/dist/src/process/studio-settings.test.js +16 -0
- package/dist/src/process/studio-settings.test.js.map +1 -0
- package/dist/src/process/studio-window-state.d.ts +13 -0
- package/dist/src/process/studio-window-state.d.ts.map +1 -0
- package/dist/src/process/studio-window-state.js +48 -0
- package/dist/src/process/studio-window-state.js.map +1 -0
- package/dist/src/server/studio-bridge-server.d.ts +3 -1
- package/dist/src/server/studio-bridge-server.d.ts.map +1 -1
- package/dist/src/server/studio-bridge-server.js +12 -4
- package/dist/src/server/studio-bridge-server.js.map +1 -1
- package/dist/src/server/web-socket-protocol.d.ts +173 -6
- package/dist/src/server/web-socket-protocol.d.ts.map +1 -1
- package/dist/src/server/web-socket-protocol.js +244 -1
- package/dist/src/server/web-socket-protocol.js.map +1 -1
- package/dist/src/server/web-socket-protocol.test.js +48 -0
- package/dist/src/server/web-socket-protocol.test.js.map +1 -1
- package/dist/src/test/e2e/persistent-session.test.js +121 -0
- package/dist/src/test/e2e/persistent-session.test.js.map +1 -1
- package/dist/src/test/e2e/split-server.test.d.ts +3 -5
- package/dist/src/test/e2e/split-server.test.d.ts.map +1 -1
- package/dist/src/test/e2e/split-server.test.js +3 -5
- package/dist/src/test/e2e/split-server.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/docker/Dockerfile +3 -0
- package/package.json +8 -6
- package/src/bridge/bridge-connection.test.ts +126 -0
- package/src/bridge/bridge-connection.ts +217 -22
- package/src/bridge/bridge-session.test.ts +6 -4
- package/src/bridge/bridge-session.ts +273 -28
- package/src/bridge/index.ts +3 -0
- package/src/bridge/internal/bridge-client.test.ts +23 -0
- package/src/bridge/internal/bridge-client.ts +25 -7
- package/src/bridge/internal/bridge-host.test.ts +38 -0
- package/src/bridge/internal/bridge-host.ts +47 -0
- package/src/bridge/internal/hand-off.ts +4 -1
- package/src/bridge/internal/host-protocol.test.ts +15 -0
- package/src/bridge/internal/host-protocol.ts +148 -37
- package/src/bridge/internal/instance-state.ts +34 -0
- package/src/bridge/internal/transport-client.ts +2 -1
- package/src/bridge/internal/transport-server.ts +5 -1
- package/src/bridge/types.ts +47 -2
- package/src/cli/adapters/cli-command-adapter-handler.test.ts +28 -0
- package/src/cli/adapters/cli-command-adapter.ts +150 -18
- package/src/cli/adapters/group-builder.test.ts +34 -2
- package/src/cli/adapters/group-builder.ts +52 -20
- package/src/cli/adapters/help-formatter.test.ts +1 -1
- package/src/cli/adapters/help-formatter.ts +4 -3
- package/src/cli/adapters/playtest-edit-guard.test.ts +54 -0
- package/src/cli/args/global-args.ts +1 -1
- package/src/cli/cli-output-context.test.ts +55 -0
- package/src/cli/cli-output-context.ts +50 -0
- package/src/cli/cli.ts +49 -17
- package/src/cli/managed-studio-run.test.ts +71 -0
- package/src/cli/managed-studio-run.ts +135 -0
- package/src/cli/resolve-session.test.ts +1 -1
- package/src/cli/resolve-session.ts +1 -1
- package/src/cli/script-executor.ts +99 -51
- package/src/commands/console/exec/exec.test.ts +96 -3
- package/src/commands/console/exec/exec.ts +59 -6
- package/src/commands/console/exec/execute.lua +356 -240
- package/src/commands/console/exec/execute.spec.lua +286 -0
- package/src/commands/console/logs/logs.test.ts +76 -4
- package/src/commands/console/logs/logs.ts +132 -17
- package/src/commands/console/logs/query-logs.lua +85 -52
- package/src/commands/console/logs/query-logs.spec.lua +132 -0
- package/src/commands/explorer/export/export-instances.lua +108 -0
- package/src/commands/explorer/export/export-instances.spec.lua +87 -0
- package/src/commands/explorer/export/export.test.ts +27 -0
- package/src/commands/explorer/export/export.ts +60 -0
- package/src/commands/explorer/find/find-instances.lua +174 -0
- package/src/commands/explorer/find/find-instances.spec.lua +122 -0
- package/src/commands/explorer/find/find.test.ts +53 -0
- package/src/commands/explorer/find/find.ts +113 -0
- package/src/commands/explorer/grep/grep-scripts.lua +214 -0
- package/src/commands/explorer/grep/grep-scripts.spec.lua +130 -0
- package/src/commands/explorer/grep/grep.test.ts +45 -0
- package/src/commands/explorer/grep/grep.ts +93 -0
- package/src/commands/explorer/import/import-instances.lua +106 -0
- package/src/commands/explorer/import/import-instances.spec.lua +84 -0
- package/src/commands/explorer/import/import.test.ts +60 -0
- package/src/commands/explorer/import/import.ts +66 -0
- package/src/commands/explorer/query/query-data-model.lua +124 -152
- package/src/commands/explorer/query/query-data-model.spec.lua +150 -0
- package/src/commands/formatters.test.ts +7 -1
- package/src/commands/framework/action-loader.test.ts +28 -0
- package/src/commands/framework/action-loader.ts +10 -2
- package/src/commands/framework/arg-builder.ts +26 -3
- package/src/commands/framework/define-command.ts +32 -3
- package/src/commands/framework/index.ts +1 -0
- package/src/commands/index.ts +50 -0
- package/src/commands/linux/inject-credentials/inject-credentials.ts +1 -3
- package/src/commands/linux/setup/setup.ts +1 -3
- package/src/commands/linux/status/status.ts +105 -98
- package/src/commands/playtest/player/add/add.ts +85 -0
- package/src/commands/playtest/player/list/list.ts +57 -0
- package/src/commands/playtest/player/playtest-players.test.ts +146 -0
- package/src/commands/playtest/player/playtest-players.ts +270 -0
- package/src/commands/playtest/player/remove/remove.ts +78 -0
- package/src/commands/playtest/playtest-sessions.test.ts +143 -0
- package/src/commands/playtest/playtest-sessions.ts +417 -0
- package/src/commands/playtest/restart/restart.ts +199 -0
- package/src/commands/playtest/run/playtest-runner.luau +264 -0
- package/src/commands/playtest/run/playtest-runner.spec.lua +71 -0
- package/src/commands/playtest/run/run-playtest-script.lua +139 -0
- package/src/commands/playtest/run/run-playtest-script.spec.lua +43 -0
- package/src/commands/playtest/run/run.ts +239 -0
- package/src/commands/playtest/start/start-playtest.lua +108 -0
- package/src/commands/playtest/start/start.ts +138 -0
- package/src/commands/playtest/stop/end-playtest.lua +47 -0
- package/src/commands/playtest/stop/stop.ts +51 -0
- package/src/commands/process/close/close.test.ts +250 -0
- package/src/commands/process/close/close.ts +183 -17
- package/src/commands/process/info/info.test.ts +198 -44
- package/src/commands/process/info/info.ts +204 -15
- package/src/commands/process/info/query-state.lua +42 -29
- package/src/commands/process/info/query-state.spec.lua +39 -0
- package/src/commands/process/launch/launch.test.ts +138 -21
- package/src/commands/process/launch/launch.ts +69 -9
- package/src/commands/process/list/list.test.ts +1 -0
- package/src/commands/process/list/list.ts +14 -6
- package/src/commands/process/run/run.ts +26 -5
- package/src/commands/serve/serve.test.ts +17 -2
- package/src/commands/serve/serve.ts +25 -6
- package/src/commands/viewport/screenshot/capture-screenshot.lua +175 -182
- package/src/commands/viewport/screenshot/capture-screenshot.spec.lua +129 -0
- package/src/commands/viewport/screenshot/screenshot.test.ts +7 -10
- package/src/commands/viewport/screenshot/screenshot.ts +6 -1
- package/src/deploy/deploy-places.test.ts +127 -0
- package/src/deploy/deploy-places.ts +229 -0
- package/src/index.ts +11 -0
- package/src/plugin/persistent-plugin-installer.test.ts +4 -0
- package/src/plugin/persistent-plugin-installer.ts +2 -1
- package/src/plugin/plugin-build-version.test.ts +55 -0
- package/src/plugin/plugin-build-version.ts +53 -0
- package/src/plugin/plugin-injector.ts +7 -1
- package/src/process/studio-process-closer.test.ts +107 -0
- package/src/process/studio-process-closer.ts +187 -0
- package/src/process/studio-settings.test.ts +18 -0
- package/src/process/studio-settings.ts +61 -0
- package/src/process/studio-window-state.ts +65 -0
- package/src/server/studio-bridge-server.ts +15 -4
- package/src/server/web-socket-protocol.test.ts +55 -0
- package/src/server/web-socket-protocol.ts +468 -8
- package/src/test/e2e/persistent-session.test.ts +137 -0
- package/src/test/e2e/split-server.test.ts +3 -5
- package/templates/studio-bridge-plugin/default.project.json +3 -0
- package/templates/studio-bridge-plugin/src/Shared/ActionRouter.lua +208 -159
- package/templates/studio-bridge-plugin/src/Shared/ActionRouter.spec.lua +418 -0
- package/templates/studio-bridge-plugin/src/Shared/BuiltInActions.lua +94 -0
- package/templates/studio-bridge-plugin/src/Shared/BuiltInActions.spec.lua +166 -0
- package/templates/studio-bridge-plugin/src/Shared/DiscoveryStateMachine.lua +118 -137
- package/templates/studio-bridge-plugin/src/Shared/DiscoveryStateMachine.spec.lua +228 -0
- package/templates/studio-bridge-plugin/src/Shared/MessageBuffer.lua +80 -109
- package/templates/studio-bridge-plugin/src/Shared/MessageBuffer.spec.lua +116 -0
- package/templates/studio-bridge-plugin/src/Shared/StudioBridgeConnection.lua +233 -0
- package/templates/studio-bridge-plugin/src/Shared/StudioBridgeConnection.spec.lua +239 -0
- package/templates/studio-bridge-plugin/src/Shared/StudioBridgeLogUtils.lua +53 -0
- package/templates/studio-bridge-plugin/src/Shared/StudioBridgeLogUtils.spec.lua +27 -0
- package/templates/studio-bridge-plugin/src/Shared/StudioBridgeSessionUtils.lua +164 -0
- package/templates/studio-bridge-plugin/src/Shared/StudioBridgeSessionUtils.spec.lua +159 -0
- package/templates/studio-bridge-plugin/src/StudioBridgePlugin.server.lua +266 -368
- package/templates/studio-bridge-plugin/src/Vendor/png.lua +8 -2
- package/test/default.project.json +41 -0
- package/test/jest.config.lua +3 -0
- package/test/scripts/Server/ServerMain.server.lua +10 -0
- package/tsconfig.json +1 -0
- package/templates/studio-bridge-plugin/src/Shared/Protocol.lua +0 -132
- package/templates/studio-bridge-plugin-test/test/actions.test.lua +0 -580
- package/templates/studio-bridge-plugin-test/test/discovery.test.lua +0 -622
- package/templates/studio-bridge-plugin-test/test/execute-handler.test.lua +0 -517
- package/templates/studio-bridge-plugin-test/test/hash-lifecycle.test.lua +0 -814
- package/templates/studio-bridge-plugin-test/test/integration.test.lua +0 -1020
- package/templates/studio-bridge-plugin-test/test/new-actions.test.lua +0 -339
- package/templates/studio-bridge-plugin-test/test/plugin-entry.test.lua +0 -493
- package/templates/studio-bridge-plugin-test/test/protocol.test.lua +0 -506
- package/templates/studio-bridge-plugin-test/test/roblox-mocks.lua +0 -108
- 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
|