From 61696c8f3f9dcfa5be372a11a7e9af1acf81934a Mon Sep 17 00:00:00 2001 From: Kacper Wojciechowski <39823706+jog1t@users.noreply.github.com> Date: Wed, 12 Nov 2025 20:56:36 +0100 Subject: [PATCH] refactor: performance improvements for inspector --- engine/sdks/go/api-full/client/client.go | 6 + engine/sdks/go/api-full/namespaces.go | 10 +- engine/sdks/go/api-full/namespaces/client.go | 3 + engine/sdks/go/api-full/runners.go | 14 +- engine/sdks/go/api-full/runners/client.go | 3 + engine/sdks/go/api-full/types.go | 28 +- engine/sdks/rust/api-full/rust/Cargo.toml | 2 +- engine/sdks/rust/api-full/rust/README.md | 4 +- .../rust/api-full/rust/docs/ActorsListApi.md | 5 +- .../rust/api-full/rust/docs/NamespacesApi.md | 5 +- .../rust/docs/RunnerConfigsListApi.md | 5 +- .../rust/api-full/rust/docs/RunnersApi.md | 5 +- .../rust/src/apis/actors_create_api.rs | 2 +- .../rust/src/apis/actors_delete_api.rs | 2 +- .../rust/src/apis/actors_get_or_create_api.rs | 2 +- .../rust/src/apis/actors_kv_get_api.rs | 2 +- .../api-full/rust/src/apis/actors_list_api.rs | 11 +- .../rust/src/apis/actors_list_names_api.rs | 2 +- .../api-full/rust/src/apis/configuration.rs | 4 +- .../api-full/rust/src/apis/datacenters_api.rs | 2 +- .../rust/api-full/rust/src/apis/health_api.rs | 2 +- .../api-full/rust/src/apis/metadata_api.rs | 2 +- .../api-full/rust/src/apis/namespaces_api.rs | 11 +- .../src/apis/runner_configs_delete_api.rs | 2 +- .../rust/src/apis/runner_configs_list_api.rs | 11 +- .../runner_configs_refresh_metadata_api.rs | 2 +- ...ner_configs_serverless_health_check_api.rs | 2 +- .../src/apis/runner_configs_upsert_api.rs | 2 +- .../api-full/rust/src/apis/runners_api.rs | 11 +- .../rust/api-full/rust/src/models/actor.rs | 2 +- .../api-full/rust/src/models/actor_name.rs | 2 +- .../rust/src/models/actors_create_request.rs | 2 +- .../rust/src/models/actors_create_response.rs | 2 +- .../models/actors_get_or_create_request.rs | 2 +- .../models/actors_get_or_create_response.rs | 2 +- .../rust/src/models/actors_kv_get_response.rs | 2 +- .../src/models/actors_list_names_response.rs | 2 +- .../rust/src/models/actors_list_response.rs | 2 +- .../api-full/rust/src/models/crash_policy.rs | 2 +- .../api-full/rust/src/models/datacenter.rs | 2 +- .../rust/src/models/datacenter_health.rs | 2 +- .../src/models/datacenters_list_response.rs | 2 +- .../rust/src/models/health_fanout_response.rs | 2 +- .../rust/src/models/health_response.rs | 2 +- .../api-full/rust/src/models/health_status.rs | 2 +- .../rust/src/models/metadata_get_response.rs | 2 +- .../api-full/rust/src/models/namespace.rs | 2 +- .../src/models/namespace_list_response.rs | 2 +- .../src/models/namespaces_create_request.rs | 2 +- .../src/models/namespaces_create_response.rs | 2 +- .../api-full/rust/src/models/pagination.rs | 2 +- .../rust/api-full/rust/src/models/runner.rs | 2 +- .../api-full/rust/src/models/runner_config.rs | 2 +- .../rust/src/models/runner_config_kind.rs | 2 +- .../src/models/runner_config_kind_one_of.rs | 2 +- .../src/models/runner_config_kind_one_of_1.rs | 2 +- .../runner_config_kind_one_of_1_serverless.rs | 2 +- .../rust/src/models/runner_config_variant.rs | 2 +- .../models/runner_configs_list_response.rs | 2 +- ...figs_list_response_runner_configs_value.rs | 2 +- ...configs_serverless_health_check_request.rs | 2 +- ...onfigs_serverless_health_check_response.rs | 2 +- ...serverless_health_check_response_one_of.rs | 2 +- ...rverless_health_check_response_one_of_1.rs | 2 +- ..._health_check_response_one_of_1_failure.rs | 2 +- ...ss_health_check_response_one_of_success.rs | 2 +- ...unner_configs_serverless_metadata_error.rs | 2 +- ...onfigs_serverless_metadata_error_one_of.rs | 2 +- ...figs_serverless_metadata_error_one_of_1.rs | 2 +- ...figs_serverless_metadata_error_one_of_2.rs | 2 +- ...figs_serverless_metadata_error_one_of_3.rs | 2 +- ...adata_error_one_of_3_non_success_status.rs | 2 +- ...figs_serverless_metadata_error_one_of_4.rs | 2 +- ...ta_error_one_of_4_invalid_response_json.rs | 2 +- ...figs_serverless_metadata_error_one_of_5.rs | 2 +- ..._error_one_of_5_invalid_response_schema.rs | 2 +- .../runner_configs_upsert_request_body.rs | 2 +- .../models/runner_configs_upsert_response.rs | 2 +- .../src/models/runners_list_names_response.rs | 2 +- .../rust/src/models/runners_list_response.rs | 2 +- engine/sdks/typescript/api-full/src/Client.ts | 24 +- .../api/client/requests/ActorsListRequest.ts | 6 + .../requests/RunnerConfigsListRequest.ts | 4 + .../api/resources/namespaces/client/Client.ts | 14 +- .../client/requests/NamespacesListRequest.ts | 6 + .../api/resources/runners/client/Client.ts | 14 +- .../client/requests/RunnersListRequest.ts | 6 + engine/sdks/typescript/runner/src/tunnel.ts | 2 +- frontend/package.json | 4 + .../components/src/actors/actor-build.tsx | 80 -- .../src/actors/actor-config-tab.tsx | 31 - .../src/actors/actor-connections-tab.tsx | 63 - .../components/src/actors/actor-context.tsx | 437 ------- .../components/src/actors/actor-cpu-stats.tsx | 149 --- .../actors/actor-details-settings-button.tsx | 58 - .../src/actors/actor-details-settings.tsx | 41 - .../src/actors/actor-download-logs-button.tsx | 61 - .../src/actors/actor-editable-state.tsx | 114 -- .../components/src/actors/actor-general.tsx | 87 -- .../components/src/actors/actor-logs-tab.tsx | 100 -- .../components/src/actors/actor-logs.tsx | 219 ---- .../src/actors/actor-memory-stats.tsx | 137 --- .../src/actors/actor-metrics-tab.tsx | 25 - .../components/src/actors/actor-metrics.tsx | 704 ----------- .../components/src/actors/actor-network.tsx | 143 --- .../components/src/actors/actor-not-found.tsx | 73 -- .../components/src/actors/actor-region.tsx | 63 - .../components/src/actors/actor-runtime.tsx | 90 -- .../actors/actor-state-change-indicator.tsx | 39 - .../components/src/actors/actor-state-tab.tsx | 61 - .../src/actors/actor-status-indicator.tsx | 85 -- .../src/actors/actor-status-label.tsx | 27 - .../components/src/actors/actor-status.tsx | 53 - .../src/actors/actor-stop-button.tsx | 47 - .../src/actors/actor-tags-select.tsx | 107 -- .../components/src/actors/actor-tags.tsx | 141 --- .../src/actors/actors-actor-details.tsx | 240 ---- .../src/actors/actors-actor-not-found.tsx | 33 - .../src/actors/actors-layout-context.tsx | 38 - .../components/src/actors/actors-layout.tsx | 34 - .../src/actors/actors-list-panel.tsx | 5 - .../src/actors/actors-list-preview.tsx | 158 --- .../components/src/actors/actors-list-row.tsx | 168 --- .../components/src/actors/actors-list.tsx | 489 -------- .../actors/actors-sidebar-toggle-button.tsx | 23 - .../actors/actors-view-context-provider.tsx | 54 - .../components/src/actors/build-select.tsx | 70 -- .../actors/console/actor-console-input.tsx | 58 - .../console/actor-console-log-formatted.tsx | 27 - .../src/actors/console/actor-console-log.tsx | 86 -- .../src/actors/console/actor-console-logs.tsx | 46 - .../actors/console/actor-console-message.tsx | 100 -- .../src/actors/console/actor-console.tsx | 50 - .../src/actors/console/actor-inspector.tsx | 30 - .../src/actors/console/repl-input.tsx | 101 -- .../src/actors/create-actor-button.tsx | 61 - .../current-environment-version-title.tsx | 36 - .../actors/dialogs/create-actor-dialog.tsx | 63 - .../src/actors/dialogs/go-to-actor-dialog.tsx | 35 - .../actors/dynamic-servers-feature-card.tsx | 32 - .../src/actors/environment-select.tsx | 73 -- .../src/actors/environment-version-title.tsx | 18 - .../src/actors/form/actor-create-form.tsx | 150 --- .../src/actors/form/build-tags-form.tsx | 160 --- .../src/actors/form/go-to-actor-form.tsx | 48 - .../components/src/actors/get-started.tsx | 95 -- .../components/src/actors/getting-started.tsx | 21 - .../src/actors/go-to-actor-button.tsx | 25 - .../src/actors/group-project-select.tsx | 68 -- .../components/src/actors/hooks/index.ts | 1 - .../src/actors/hooks/use-dialog.tsx | 159 --- .../packages/components/src/actors/index.tsx | 17 - .../matchmaker-lobby-config-settings-card.tsx | 59 - .../actors/project-builds-table-actions.tsx | 41 - .../project-environments-table-actions.tsx | 24 - .../src/actors/project-logo-settings-card.tsx | 46 - .../components/src/actors/project-select.tsx | 90 -- .../src/actors/project-table-actions.tsx | 24 - .../components/src/actors/project-tile.tsx | 34 - .../components/src/actors/region-select.tsx | 45 - .../src/actors/worker/actor-repl.worker.ts | 354 ------ .../actors/worker/actor-worker-container.ts | 330 ------ .../actors/worker/actor-worker-context.tsx | 158 --- .../src/actors/worker/actor-worker-schema.ts | 94 -- .../src/actors/worker/actor-worker-status.tsx | 41 - frontend/src/app.tsx | 9 + frontend/src/app/actor-builds-list.tsx | 8 +- frontend/src/app/actors.tsx | 24 +- frontend/src/app/build-prefiller.tsx | 2 +- frontend/src/app/connect.tsx | 9 +- frontend/src/app/credentials-context.tsx | 19 - .../data-providers/default-data-provider.tsx | 191 ++- .../data-providers/engine-data-provider.tsx | 145 +-- .../inspector-data-provider.tsx | 183 +-- .../connect-manual-serverfull-frame.tsx | 8 +- .../connect-manual-serverless-frame.tsx | 4 +- .../dialogs/connect-quick-railway-frame.tsx | 4 +- .../dialogs/connect-quick-vercel-frame.tsx | 4 +- .../src/app/dialogs/connect-railway-frame.tsx | 4 +- .../src/app/dialogs/connect-vercel-frame.tsx | 4 +- .../forms/connect-manual-serverless-form.tsx | 2 +- frontend/src/app/inspector-root.tsx | 79 +- frontend/src/app/layout.tsx | 49 +- .../src/components/actors/actor-build.tsx | 40 - .../actors/actor-clear-events-log-button.tsx | 40 +- .../components/actors/actor-config-tab.tsx | 4 - .../actors/actor-connections-tab.tsx | 36 +- .../src/components/actors/actor-context.tsx | 4 - .../src/components/actors/actor-database.tsx | 20 +- .../src/components/actors/actor-db-tab.tsx | 6 +- .../actors/actor-editable-state.tsx | 25 +- .../components/actors/actor-events-list.tsx | 9 +- .../components/actors/actor-events-tab.tsx | 4 +- .../src/components/actors/actor-events.tsx | 7 +- .../actors/actor-filters-context.tsx | 16 +- .../src/components/actors/actor-network.tsx | 143 --- .../src/components/actors/actor-not-found.tsx | 12 +- .../actors/actor-queries-context.tsx | 270 ----- .../src/components/actors/actor-region.tsx | 4 +- .../src/components/actors/actor-runtime.tsx | 16 - .../src/components/actors/actor-state-tab.tsx | 12 +- .../components/actors/actor-status-label.tsx | 8 +- .../components/actors/actor-stop-button.tsx | 1 + .../actors/actors-actor-details.tsx | 229 ++-- .../src/components/actors/actors-list-row.tsx | 5 +- .../actors/console/actor-console-input.tsx | 8 +- .../actors/console/actor-console.tsx | 15 +- .../components/actors/crash-policy-select.tsx | 12 +- .../actors/dialogs/create-actor-dialog.tsx | 10 +- .../actors/form/actor-create-form.tsx | 17 +- .../actors/guard-connectable-inspector.tsx | 246 ++-- frontend/src/components/actors/index.ts | 2 - .../components/actors/inspector-context.tsx | 540 +++++++++ .../src/components/actors/queries/actor.ts | 232 ---- .../src/components/actors/queries/index.ts | 133 +-- .../src/components/actors/region-select.tsx | 11 +- .../actors/worker/actor-repl.worker.ts | 69 +- .../actors/worker/actor-worker-container.ts | 28 +- .../actors/worker/actor-worker-context.tsx | 51 +- .../actors/worker/actor-worker-schema.ts | 22 +- frontend/src/components/connection-form.tsx | 54 +- .../src/components/hooks/use-websocket.tsx | 130 +++ frontend/src/components/lib/utils.ts | 4 + frontend/src/queries/actor-inspector.ts | 49 - frontend/src/queries/global.ts | 7 + frontend/src/routes/_context.tsx | 5 +- .../projects.$project/ns.$namespace/index.tsx | 6 +- frontend/src/routes/_context/index.tsx | 6 +- .../src/utils/use-railway-template-link.ts | 2 +- pnpm-lock.yaml | 28 + .../cloudflare-workers/src/manager-driver.ts | 4 - .../fixtures/driver-test-suite/sleep.ts | 2 +- .../packages/rivetkit/package.json | 2 +- .../rivetkit/schemas/actor-inspector/v1.bare | 146 +++ .../packages/rivetkit/scripts/dump-openapi.ts | 1 - .../src/actor/instance/connection-manager.ts | 6 +- .../rivetkit/src/actor/instance/mod.ts | 91 +- .../src/actor/router-websocket-endpoints.ts | 28 +- .../packages/rivetkit/src/actor/router.ts | 28 - .../rivetkit/src/driver-test-suite/mod.ts | 2 - .../test-inline-client-driver.ts | 2 - .../tests/actor-inspector.ts | 1030 ++++++----------- .../src/drivers/file-system/global-state.ts | 14 - .../src/drivers/file-system/manager.ts | 75 -- .../rivetkit/src/inspector/actor-inspector.ts | 229 ++++ .../packages/rivetkit/src/inspector/actor.ts | 339 ------ .../packages/rivetkit/src/inspector/config.ts | 11 +- .../rivetkit/src/inspector/handler.ts | 156 +++ .../rivetkit/src/inspector/manager.ts | 93 -- .../packages/rivetkit/src/inspector/mod.ts | 5 +- .../rivetkit/src/inspector/protocol/actor.ts | 10 - .../rivetkit/src/inspector/protocol/common.ts | 196 ---- .../src/inspector/protocol/manager.ts | 10 - .../rivetkit/src/inspector/protocol/mod.ts | 2 - .../packages/rivetkit/src/inspector/utils.ts | 61 +- .../packages/rivetkit/src/manager/driver.ts | 15 - .../packages/rivetkit/src/manager/router.ts | 31 +- .../packages/rivetkit/src/registry/mod.ts | 20 +- .../rivetkit/src/remote-manager-driver/mod.ts | 4 - .../src/schemas/actor-inspector/mod.ts | 1 + .../src/schemas/actor-inspector/versioned.ts | 23 + .../packages/rivetkit/src/test/mod.ts | 5 - 262 files changed, 2603 insertions(+), 11176 deletions(-) delete mode 100644 frontend/packages/components/src/actors/actor-build.tsx delete mode 100644 frontend/packages/components/src/actors/actor-config-tab.tsx delete mode 100644 frontend/packages/components/src/actors/actor-connections-tab.tsx delete mode 100644 frontend/packages/components/src/actors/actor-context.tsx delete mode 100644 frontend/packages/components/src/actors/actor-cpu-stats.tsx delete mode 100644 frontend/packages/components/src/actors/actor-details-settings-button.tsx delete mode 100644 frontend/packages/components/src/actors/actor-details-settings.tsx delete mode 100644 frontend/packages/components/src/actors/actor-download-logs-button.tsx delete mode 100644 frontend/packages/components/src/actors/actor-editable-state.tsx delete mode 100644 frontend/packages/components/src/actors/actor-general.tsx delete mode 100644 frontend/packages/components/src/actors/actor-logs-tab.tsx delete mode 100644 frontend/packages/components/src/actors/actor-logs.tsx delete mode 100644 frontend/packages/components/src/actors/actor-memory-stats.tsx delete mode 100644 frontend/packages/components/src/actors/actor-metrics-tab.tsx delete mode 100644 frontend/packages/components/src/actors/actor-metrics.tsx delete mode 100644 frontend/packages/components/src/actors/actor-network.tsx delete mode 100644 frontend/packages/components/src/actors/actor-not-found.tsx delete mode 100644 frontend/packages/components/src/actors/actor-region.tsx delete mode 100644 frontend/packages/components/src/actors/actor-runtime.tsx delete mode 100644 frontend/packages/components/src/actors/actor-state-change-indicator.tsx delete mode 100644 frontend/packages/components/src/actors/actor-state-tab.tsx delete mode 100644 frontend/packages/components/src/actors/actor-status-indicator.tsx delete mode 100644 frontend/packages/components/src/actors/actor-status-label.tsx delete mode 100644 frontend/packages/components/src/actors/actor-status.tsx delete mode 100644 frontend/packages/components/src/actors/actor-stop-button.tsx delete mode 100644 frontend/packages/components/src/actors/actor-tags-select.tsx delete mode 100644 frontend/packages/components/src/actors/actor-tags.tsx delete mode 100644 frontend/packages/components/src/actors/actors-actor-details.tsx delete mode 100644 frontend/packages/components/src/actors/actors-actor-not-found.tsx delete mode 100644 frontend/packages/components/src/actors/actors-layout-context.tsx delete mode 100644 frontend/packages/components/src/actors/actors-layout.tsx delete mode 100644 frontend/packages/components/src/actors/actors-list-panel.tsx delete mode 100644 frontend/packages/components/src/actors/actors-list-preview.tsx delete mode 100644 frontend/packages/components/src/actors/actors-list-row.tsx delete mode 100644 frontend/packages/components/src/actors/actors-list.tsx delete mode 100644 frontend/packages/components/src/actors/actors-sidebar-toggle-button.tsx delete mode 100644 frontend/packages/components/src/actors/actors-view-context-provider.tsx delete mode 100644 frontend/packages/components/src/actors/build-select.tsx delete mode 100644 frontend/packages/components/src/actors/console/actor-console-input.tsx delete mode 100644 frontend/packages/components/src/actors/console/actor-console-log-formatted.tsx delete mode 100644 frontend/packages/components/src/actors/console/actor-console-log.tsx delete mode 100644 frontend/packages/components/src/actors/console/actor-console-logs.tsx delete mode 100644 frontend/packages/components/src/actors/console/actor-console-message.tsx delete mode 100644 frontend/packages/components/src/actors/console/actor-console.tsx delete mode 100644 frontend/packages/components/src/actors/console/actor-inspector.tsx delete mode 100644 frontend/packages/components/src/actors/console/repl-input.tsx delete mode 100644 frontend/packages/components/src/actors/create-actor-button.tsx delete mode 100644 frontend/packages/components/src/actors/current-environment-version-title.tsx delete mode 100644 frontend/packages/components/src/actors/dialogs/create-actor-dialog.tsx delete mode 100644 frontend/packages/components/src/actors/dialogs/go-to-actor-dialog.tsx delete mode 100644 frontend/packages/components/src/actors/dynamic-servers-feature-card.tsx delete mode 100644 frontend/packages/components/src/actors/environment-select.tsx delete mode 100644 frontend/packages/components/src/actors/environment-version-title.tsx delete mode 100644 frontend/packages/components/src/actors/form/actor-create-form.tsx delete mode 100644 frontend/packages/components/src/actors/form/build-tags-form.tsx delete mode 100644 frontend/packages/components/src/actors/form/go-to-actor-form.tsx delete mode 100644 frontend/packages/components/src/actors/get-started.tsx delete mode 100644 frontend/packages/components/src/actors/getting-started.tsx delete mode 100644 frontend/packages/components/src/actors/go-to-actor-button.tsx delete mode 100644 frontend/packages/components/src/actors/group-project-select.tsx delete mode 100644 frontend/packages/components/src/actors/hooks/index.ts delete mode 100644 frontend/packages/components/src/actors/hooks/use-dialog.tsx delete mode 100644 frontend/packages/components/src/actors/index.tsx delete mode 100644 frontend/packages/components/src/actors/matchmaker-lobby-config-settings-card.tsx delete mode 100644 frontend/packages/components/src/actors/project-builds-table-actions.tsx delete mode 100644 frontend/packages/components/src/actors/project-environments-table-actions.tsx delete mode 100644 frontend/packages/components/src/actors/project-logo-settings-card.tsx delete mode 100644 frontend/packages/components/src/actors/project-select.tsx delete mode 100644 frontend/packages/components/src/actors/project-table-actions.tsx delete mode 100644 frontend/packages/components/src/actors/project-tile.tsx delete mode 100644 frontend/packages/components/src/actors/region-select.tsx delete mode 100644 frontend/packages/components/src/actors/worker/actor-repl.worker.ts delete mode 100644 frontend/packages/components/src/actors/worker/actor-worker-container.ts delete mode 100644 frontend/packages/components/src/actors/worker/actor-worker-context.tsx delete mode 100644 frontend/packages/components/src/actors/worker/actor-worker-schema.ts delete mode 100644 frontend/packages/components/src/actors/worker/actor-worker-status.tsx delete mode 100644 frontend/src/app/credentials-context.tsx delete mode 100644 frontend/src/components/actors/actor-build.tsx delete mode 100644 frontend/src/components/actors/actor-context.tsx delete mode 100644 frontend/src/components/actors/actor-network.tsx delete mode 100644 frontend/src/components/actors/actor-queries-context.tsx delete mode 100644 frontend/src/components/actors/actor-runtime.tsx create mode 100644 frontend/src/components/actors/inspector-context.tsx delete mode 100644 frontend/src/components/actors/queries/actor.ts create mode 100644 frontend/src/components/hooks/use-websocket.tsx delete mode 100644 frontend/src/queries/actor-inspector.ts create mode 100644 rivetkit-typescript/packages/rivetkit/schemas/actor-inspector/v1.bare create mode 100644 rivetkit-typescript/packages/rivetkit/src/inspector/actor-inspector.ts delete mode 100644 rivetkit-typescript/packages/rivetkit/src/inspector/actor.ts create mode 100644 rivetkit-typescript/packages/rivetkit/src/inspector/handler.ts delete mode 100644 rivetkit-typescript/packages/rivetkit/src/inspector/manager.ts delete mode 100644 rivetkit-typescript/packages/rivetkit/src/inspector/protocol/actor.ts delete mode 100644 rivetkit-typescript/packages/rivetkit/src/inspector/protocol/common.ts delete mode 100644 rivetkit-typescript/packages/rivetkit/src/inspector/protocol/manager.ts delete mode 100644 rivetkit-typescript/packages/rivetkit/src/inspector/protocol/mod.ts create mode 100644 rivetkit-typescript/packages/rivetkit/src/schemas/actor-inspector/mod.ts create mode 100644 rivetkit-typescript/packages/rivetkit/src/schemas/actor-inspector/versioned.ts diff --git a/engine/sdks/go/api-full/client/client.go b/engine/sdks/go/api-full/client/client.go index cf03030743..d7e9e14f61 100644 --- a/engine/sdks/go/api-full/client/client.go +++ b/engine/sdks/go/api-full/client/client.go @@ -85,6 +85,9 @@ func (c *Client) ActorsList(ctx context.Context, request *sdk.ActorsListRequest) if request.ActorIds != nil { queryParams.Add("actor_ids", fmt.Sprintf("%v", *request.ActorIds)) } + for _, value := range request.ActorId { + queryParams.Add("actor_id", fmt.Sprintf("%v", *value)) + } if request.IncludeDestroyed != nil { queryParams.Add("include_destroyed", fmt.Sprintf("%v", *request.IncludeDestroyed)) } @@ -327,6 +330,9 @@ func (c *Client) RunnerConfigsList(ctx context.Context, request *sdk.RunnerConfi if request.RunnerNames != nil { queryParams.Add("runner_names", fmt.Sprintf("%v", *request.RunnerNames)) } + for _, value := range request.RunnerName { + queryParams.Add("runner_name", fmt.Sprintf("%v", *value)) + } if len(queryParams) > 0 { endpointURL += "?" + queryParams.Encode() } diff --git a/engine/sdks/go/api-full/namespaces.go b/engine/sdks/go/api-full/namespaces.go index 2b81652768..80041a2d61 100644 --- a/engine/sdks/go/api-full/namespaces.go +++ b/engine/sdks/go/api-full/namespaces.go @@ -8,8 +8,10 @@ type NamespacesCreateRequest struct { } type NamespacesListRequest struct { - Limit *int `json:"-"` - Cursor *string `json:"-"` - Name *string `json:"-"` - NamespaceIds *string `json:"-"` + Limit *int `json:"-"` + Cursor *string `json:"-"` + Name *string `json:"-"` + // Deprecated. + NamespaceIds *string `json:"-"` + NamespaceId []*RivetId `json:"-"` } diff --git a/engine/sdks/go/api-full/namespaces/client.go b/engine/sdks/go/api-full/namespaces/client.go index ca1a8c547c..7057013ce1 100644 --- a/engine/sdks/go/api-full/namespaces/client.go +++ b/engine/sdks/go/api-full/namespaces/client.go @@ -49,6 +49,9 @@ func (c *Client) List(ctx context.Context, request *sdk.NamespacesListRequest) ( if request.NamespaceIds != nil { queryParams.Add("namespace_ids", fmt.Sprintf("%v", *request.NamespaceIds)) } + for _, value := range request.NamespaceId { + queryParams.Add("namespace_id", fmt.Sprintf("%v", *value)) + } if len(queryParams) > 0 { endpointURL += "?" + queryParams.Encode() } diff --git a/engine/sdks/go/api-full/runners.go b/engine/sdks/go/api-full/runners.go index 6e4bcd3d42..fcbb8b6496 100644 --- a/engine/sdks/go/api-full/runners.go +++ b/engine/sdks/go/api-full/runners.go @@ -3,12 +3,14 @@ package api type RunnersListRequest struct { - Namespace string `json:"-"` - Name *string `json:"-"` - RunnerIds *string `json:"-"` - IncludeStopped *bool `json:"-"` - Limit *int `json:"-"` - Cursor *string `json:"-"` + Namespace string `json:"-"` + Name *string `json:"-"` + // Deprecated. + RunnerIds *string `json:"-"` + RunnerId []*RivetId `json:"-"` + IncludeStopped *bool `json:"-"` + Limit *int `json:"-"` + Cursor *string `json:"-"` } type RunnersListNamesRequest struct { diff --git a/engine/sdks/go/api-full/runners/client.go b/engine/sdks/go/api-full/runners/client.go index fc5f83164c..9d61927840 100644 --- a/engine/sdks/go/api-full/runners/client.go +++ b/engine/sdks/go/api-full/runners/client.go @@ -44,6 +44,9 @@ func (c *Client) List(ctx context.Context, request *sdk.RunnersListRequest) (*sd if request.RunnerIds != nil { queryParams.Add("runner_ids", fmt.Sprintf("%v", *request.RunnerIds)) } + for _, value := range request.RunnerId { + queryParams.Add("runner_id", fmt.Sprintf("%v", *value)) + } if request.IncludeStopped != nil { queryParams.Add("include_stopped", fmt.Sprintf("%v", *request.IncludeStopped)) } diff --git a/engine/sdks/go/api-full/types.go b/engine/sdks/go/api-full/types.go index 83fa823cdb..ca3d807030 100644 --- a/engine/sdks/go/api-full/types.go +++ b/engine/sdks/go/api-full/types.go @@ -33,13 +33,15 @@ type ActorsGetOrCreateRequest struct { } type ActorsListRequest struct { - Namespace string `json:"-"` - Name *string `json:"-"` - Key *string `json:"-"` - ActorIds *string `json:"-"` - IncludeDestroyed *bool `json:"-"` - Limit *int `json:"-"` - Cursor *string `json:"-"` + Namespace string `json:"-"` + Name *string `json:"-"` + Key *string `json:"-"` + // Deprecated. + ActorIds *string `json:"-"` + ActorId []*RivetId `json:"-"` + IncludeDestroyed *bool `json:"-"` + Limit *int `json:"-"` + Cursor *string `json:"-"` } type ActorsListNamesRequest struct { @@ -53,11 +55,13 @@ type RunnerConfigsDeleteRequest struct { } type RunnerConfigsListRequest struct { - Namespace string `json:"-"` - Limit *int `json:"-"` - Cursor *string `json:"-"` - Variant *RunnerConfigVariant `json:"-"` - RunnerNames *string `json:"-"` + Namespace string `json:"-"` + Limit *int `json:"-"` + Cursor *string `json:"-"` + Variant *RunnerConfigVariant `json:"-"` + // Deprecated. + RunnerNames *string `json:"-"` + RunnerName []*string `json:"-"` } type RunnerConfigsRefreshMetadataRequest struct { diff --git a/engine/sdks/rust/api-full/rust/Cargo.toml b/engine/sdks/rust/api-full/rust/Cargo.toml index ae1d77a706..0b3f52e46f 100644 --- a/engine/sdks/rust/api-full/rust/Cargo.toml +++ b/engine/sdks/rust/api-full/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rivet-api-full" -version = "2.0.25-rc.1" +version = "2.0.25" authors = ["developer@rivet.gg"] description = "No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)" license = "Apache-2.0" diff --git a/engine/sdks/rust/api-full/rust/README.md b/engine/sdks/rust/api-full/rust/README.md index 767fb69fbc..162a0fe478 100644 --- a/engine/sdks/rust/api-full/rust/README.md +++ b/engine/sdks/rust/api-full/rust/README.md @@ -7,8 +7,8 @@ No description provided (generated by Openapi Generator https://github.com/opena This API client was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using the [openapi-spec](https://openapis.org) from a remote server, you can easily generate an API client. -- API version: 2.0.25-rc.1 -- Package version: 2.0.25-rc.1 +- API version: 2.0.25 +- Package version: 2.0.25 - Generator version: 7.14.0 - Build package: `org.openapitools.codegen.languages.RustClientCodegen` diff --git a/engine/sdks/rust/api-full/rust/docs/ActorsListApi.md b/engine/sdks/rust/api-full/rust/docs/ActorsListApi.md index aab1c109e5..fab37e2888 100644 --- a/engine/sdks/rust/api-full/rust/docs/ActorsListApi.md +++ b/engine/sdks/rust/api-full/rust/docs/ActorsListApi.md @@ -10,7 +10,7 @@ Method | HTTP request | Description ## actors_list -> models::ActorsListResponse actors_list(namespace, name, key, actor_ids, include_destroyed, limit, cursor) +> models::ActorsListResponse actors_list(namespace, name, key, actor_ids, actor_id, include_destroyed, limit, cursor) ## Datacenter Round Trips **If key is some & `include_destroyed` is false** 2 round trips: - namespace::ops::resolve_for_name_global - GET /actors (multiple DCs based on actor IDs) This path is optimized because we can read the actor IDs fro the key directly from Epoxy with stale consistency to determine which datacenter the actor lives in. Under most circumstances, this means we don't need to fan out to all datacenters (like normal list does). The reason `include_destroyed` has to be false is Epoxy only stores currently active actors. If `include_destroyed` is true, we show all previous iterations of actors with the same key. **Otherwise** 2 round trips: - namespace::ops::resolve_for_name_global - GET /actors (fanout) ## Optimized Alternative Routes @@ -23,7 +23,8 @@ Name | Type | Description | Required | Notes **namespace** | **String** | | [required] | **name** | Option<**String**> | | | **key** | Option<**String**> | | | -**actor_ids** | Option<**String**> | | | +**actor_ids** | Option<**String**> | Deprecated. | | +**actor_id** | Option<[**Vec**](String.md)> | | | **include_destroyed** | Option<**bool**> | | | **limit** | Option<**i32**> | | | **cursor** | Option<**String**> | | | diff --git a/engine/sdks/rust/api-full/rust/docs/NamespacesApi.md b/engine/sdks/rust/api-full/rust/docs/NamespacesApi.md index 01b82f1c0e..e1e0f7b7b3 100644 --- a/engine/sdks/rust/api-full/rust/docs/NamespacesApi.md +++ b/engine/sdks/rust/api-full/rust/docs/NamespacesApi.md @@ -39,7 +39,7 @@ Name | Type | Description | Required | Notes ## namespaces_list -> models::NamespaceListResponse namespaces_list(limit, cursor, name, namespace_ids) +> models::NamespaceListResponse namespaces_list(limit, cursor, name, namespace_ids, namespace_id) ### Parameters @@ -50,7 +50,8 @@ Name | Type | Description | Required | Notes **limit** | Option<**i32**> | | | **cursor** | Option<**String**> | | | **name** | Option<**String**> | | | -**namespace_ids** | Option<**String**> | | | +**namespace_ids** | Option<**String**> | Deprecated. | | +**namespace_id** | Option<[**Vec**](String.md)> | | | ### Return type diff --git a/engine/sdks/rust/api-full/rust/docs/RunnerConfigsListApi.md b/engine/sdks/rust/api-full/rust/docs/RunnerConfigsListApi.md index dc74a96484..7235668051 100644 --- a/engine/sdks/rust/api-full/rust/docs/RunnerConfigsListApi.md +++ b/engine/sdks/rust/api-full/rust/docs/RunnerConfigsListApi.md @@ -10,7 +10,7 @@ Method | HTTP request | Description ## runner_configs_list -> models::RunnerConfigsListResponse runner_configs_list(namespace, limit, cursor, variant, runner_names) +> models::RunnerConfigsListResponse runner_configs_list(namespace, limit, cursor, variant, runner_names, runner_name) ### Parameters @@ -22,7 +22,8 @@ Name | Type | Description | Required | Notes **limit** | Option<**i32**> | | | **cursor** | Option<**String**> | | | **variant** | Option<[**RunnerConfigVariant**](.md)> | | | -**runner_names** | Option<**String**> | | | +**runner_names** | Option<**String**> | Deprecated. | | +**runner_name** | Option<[**Vec**](String.md)> | | | ### Return type diff --git a/engine/sdks/rust/api-full/rust/docs/RunnersApi.md b/engine/sdks/rust/api-full/rust/docs/RunnersApi.md index 2f2f11aff8..c11a6bd7d7 100644 --- a/engine/sdks/rust/api-full/rust/docs/RunnersApi.md +++ b/engine/sdks/rust/api-full/rust/docs/RunnersApi.md @@ -11,7 +11,7 @@ Method | HTTP request | Description ## runners_list -> models::RunnersListResponse runners_list(namespace, name, runner_ids, include_stopped, limit, cursor) +> models::RunnersListResponse runners_list(namespace, name, runner_ids, runner_id, include_stopped, limit, cursor) ### Parameters @@ -21,7 +21,8 @@ Name | Type | Description | Required | Notes ------------- | ------------- | ------------- | ------------- | ------------- **namespace** | **String** | | [required] | **name** | Option<**String**> | | | -**runner_ids** | Option<**String**> | | | +**runner_ids** | Option<**String**> | Deprecated. | | +**runner_id** | Option<[**Vec**](String.md)> | | | **include_stopped** | Option<**bool**> | | | **limit** | Option<**i32**> | | | **cursor** | Option<**String**> | | | diff --git a/engine/sdks/rust/api-full/rust/src/apis/actors_create_api.rs b/engine/sdks/rust/api-full/rust/src/apis/actors_create_api.rs index c083aeaeea..185a9425e9 100644 --- a/engine/sdks/rust/api-full/rust/src/apis/actors_create_api.rs +++ b/engine/sdks/rust/api-full/rust/src/apis/actors_create_api.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/apis/actors_delete_api.rs b/engine/sdks/rust/api-full/rust/src/apis/actors_delete_api.rs index 1c1d860bb7..690a1ed1a9 100644 --- a/engine/sdks/rust/api-full/rust/src/apis/actors_delete_api.rs +++ b/engine/sdks/rust/api-full/rust/src/apis/actors_delete_api.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/apis/actors_get_or_create_api.rs b/engine/sdks/rust/api-full/rust/src/apis/actors_get_or_create_api.rs index 1777b5df12..9682b9a4a4 100644 --- a/engine/sdks/rust/api-full/rust/src/apis/actors_get_or_create_api.rs +++ b/engine/sdks/rust/api-full/rust/src/apis/actors_get_or_create_api.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/apis/actors_kv_get_api.rs b/engine/sdks/rust/api-full/rust/src/apis/actors_kv_get_api.rs index a18b0fc483..77e6a44ed5 100644 --- a/engine/sdks/rust/api-full/rust/src/apis/actors_kv_get_api.rs +++ b/engine/sdks/rust/api-full/rust/src/apis/actors_kv_get_api.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/apis/actors_list_api.rs b/engine/sdks/rust/api-full/rust/src/apis/actors_list_api.rs index 2cb37701c0..df59eb51e6 100644 --- a/engine/sdks/rust/api-full/rust/src/apis/actors_list_api.rs +++ b/engine/sdks/rust/api-full/rust/src/apis/actors_list_api.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ @@ -24,12 +24,13 @@ pub enum ActorsListError { /// **If key is some & `include_destroyed` is false** 2 round trips: - namespace::ops::resolve_for_name_global - GET /actors (multiple DCs based on actor IDs) This path is optimized because we can read the actor IDs fro the key directly from Epoxy with stale consistency to determine which datacenter the actor lives in. Under most circumstances, this means we don't need to fan out to all datacenters (like normal list does). The reason `include_destroyed` has to be false is Epoxy only stores currently active actors. If `include_destroyed` is true, we show all previous iterations of actors with the same key. **Otherwise** 2 round trips: - namespace::ops::resolve_for_name_global - GET /actors (fanout) ## Optimized Alternative Routes -pub async fn actors_list(configuration: &configuration::Configuration, namespace: &str, name: Option<&str>, key: Option<&str>, actor_ids: Option<&str>, include_destroyed: Option, limit: Option, cursor: Option<&str>) -> Result> { +pub async fn actors_list(configuration: &configuration::Configuration, namespace: &str, name: Option<&str>, key: Option<&str>, actor_ids: Option<&str>, actor_id: Option>, include_destroyed: Option, limit: Option, cursor: Option<&str>) -> Result> { // add a prefix to parameters to efficiently prevent name collisions let p_namespace = namespace; let p_name = name; let p_key = key; let p_actor_ids = actor_ids; + let p_actor_id = actor_id; let p_include_destroyed = include_destroyed; let p_limit = limit; let p_cursor = cursor; @@ -47,6 +48,12 @@ pub async fn actors_list(configuration: &configuration::Configuration, namespace if let Some(ref param_value) = p_actor_ids { req_builder = req_builder.query(&[("actor_ids", ¶m_value.to_string())]); } + if let Some(ref param_value) = p_actor_id { + req_builder = match "multi" { + "multi" => req_builder.query(¶m_value.into_iter().map(|p| ("actor_id".to_owned(), p.to_string())).collect::>()), + _ => req_builder.query(&[("actor_id", ¶m_value.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), + }; + } if let Some(ref param_value) = p_include_destroyed { req_builder = req_builder.query(&[("include_destroyed", ¶m_value.to_string())]); } diff --git a/engine/sdks/rust/api-full/rust/src/apis/actors_list_names_api.rs b/engine/sdks/rust/api-full/rust/src/apis/actors_list_names_api.rs index 2a4f7e18be..9879b0b902 100644 --- a/engine/sdks/rust/api-full/rust/src/apis/actors_list_names_api.rs +++ b/engine/sdks/rust/api-full/rust/src/apis/actors_list_names_api.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/apis/configuration.rs b/engine/sdks/rust/api-full/rust/src/apis/configuration.rs index cb8f4c58db..554c902b2d 100644 --- a/engine/sdks/rust/api-full/rust/src/apis/configuration.rs +++ b/engine/sdks/rust/api-full/rust/src/apis/configuration.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ @@ -40,7 +40,7 @@ impl Default for Configuration { fn default() -> Self { Configuration { base_path: "http://localhost".to_owned(), - user_agent: Some("OpenAPI-Generator/2.0.25-rc.1/rust".to_owned()), + user_agent: Some("OpenAPI-Generator/2.0.25/rust".to_owned()), client: reqwest::Client::new(), basic_auth: None, oauth_access_token: None, diff --git a/engine/sdks/rust/api-full/rust/src/apis/datacenters_api.rs b/engine/sdks/rust/api-full/rust/src/apis/datacenters_api.rs index ab0ee15ef5..a911c26b4b 100644 --- a/engine/sdks/rust/api-full/rust/src/apis/datacenters_api.rs +++ b/engine/sdks/rust/api-full/rust/src/apis/datacenters_api.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/apis/health_api.rs b/engine/sdks/rust/api-full/rust/src/apis/health_api.rs index 9c0a1dec0d..a8e93b56d4 100644 --- a/engine/sdks/rust/api-full/rust/src/apis/health_api.rs +++ b/engine/sdks/rust/api-full/rust/src/apis/health_api.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/apis/metadata_api.rs b/engine/sdks/rust/api-full/rust/src/apis/metadata_api.rs index 0dc832b880..855e59dbee 100644 --- a/engine/sdks/rust/api-full/rust/src/apis/metadata_api.rs +++ b/engine/sdks/rust/api-full/rust/src/apis/metadata_api.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/apis/namespaces_api.rs b/engine/sdks/rust/api-full/rust/src/apis/namespaces_api.rs index 20111de4f5..9ebeb44801 100644 --- a/engine/sdks/rust/api-full/rust/src/apis/namespaces_api.rs +++ b/engine/sdks/rust/api-full/rust/src/apis/namespaces_api.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ @@ -70,12 +70,13 @@ pub async fn namespaces_create(configuration: &configuration::Configuration, nam } } -pub async fn namespaces_list(configuration: &configuration::Configuration, limit: Option, cursor: Option<&str>, name: Option<&str>, namespace_ids: Option<&str>) -> Result> { +pub async fn namespaces_list(configuration: &configuration::Configuration, limit: Option, cursor: Option<&str>, name: Option<&str>, namespace_ids: Option<&str>, namespace_id: Option>) -> Result> { // add a prefix to parameters to efficiently prevent name collisions let p_limit = limit; let p_cursor = cursor; let p_name = name; let p_namespace_ids = namespace_ids; + let p_namespace_id = namespace_id; let uri_str = format!("{}/namespaces", configuration.base_path); let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); @@ -92,6 +93,12 @@ pub async fn namespaces_list(configuration: &configuration::Configuration, limit if let Some(ref param_value) = p_namespace_ids { req_builder = req_builder.query(&[("namespace_ids", ¶m_value.to_string())]); } + if let Some(ref param_value) = p_namespace_id { + req_builder = match "multi" { + "multi" => req_builder.query(¶m_value.into_iter().map(|p| ("namespace_id".to_owned(), p.to_string())).collect::>()), + _ => req_builder.query(&[("namespace_id", ¶m_value.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), + }; + } if let Some(ref user_agent) = configuration.user_agent { req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); } diff --git a/engine/sdks/rust/api-full/rust/src/apis/runner_configs_delete_api.rs b/engine/sdks/rust/api-full/rust/src/apis/runner_configs_delete_api.rs index 7090d840cd..dc1994dd23 100644 --- a/engine/sdks/rust/api-full/rust/src/apis/runner_configs_delete_api.rs +++ b/engine/sdks/rust/api-full/rust/src/apis/runner_configs_delete_api.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/apis/runner_configs_list_api.rs b/engine/sdks/rust/api-full/rust/src/apis/runner_configs_list_api.rs index 2c5ac4689f..58b870b226 100644 --- a/engine/sdks/rust/api-full/rust/src/apis/runner_configs_list_api.rs +++ b/engine/sdks/rust/api-full/rust/src/apis/runner_configs_list_api.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ @@ -23,13 +23,14 @@ pub enum RunnerConfigsListError { } -pub async fn runner_configs_list(configuration: &configuration::Configuration, namespace: &str, limit: Option, cursor: Option<&str>, variant: Option, runner_names: Option<&str>) -> Result> { +pub async fn runner_configs_list(configuration: &configuration::Configuration, namespace: &str, limit: Option, cursor: Option<&str>, variant: Option, runner_names: Option<&str>, runner_name: Option>) -> Result> { // add a prefix to parameters to efficiently prevent name collisions let p_namespace = namespace; let p_limit = limit; let p_cursor = cursor; let p_variant = variant; let p_runner_names = runner_names; + let p_runner_name = runner_name; let uri_str = format!("{}/runner-configs", configuration.base_path); let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str); @@ -47,6 +48,12 @@ pub async fn runner_configs_list(configuration: &configuration::Configuration, n if let Some(ref param_value) = p_runner_names { req_builder = req_builder.query(&[("runner_names", ¶m_value.to_string())]); } + if let Some(ref param_value) = p_runner_name { + req_builder = match "multi" { + "multi" => req_builder.query(¶m_value.into_iter().map(|p| ("runner_name".to_owned(), p.to_string())).collect::>()), + _ => req_builder.query(&[("runner_name", ¶m_value.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), + }; + } if let Some(ref user_agent) = configuration.user_agent { req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone()); } diff --git a/engine/sdks/rust/api-full/rust/src/apis/runner_configs_refresh_metadata_api.rs b/engine/sdks/rust/api-full/rust/src/apis/runner_configs_refresh_metadata_api.rs index 855885c3f1..3a88df6c48 100644 --- a/engine/sdks/rust/api-full/rust/src/apis/runner_configs_refresh_metadata_api.rs +++ b/engine/sdks/rust/api-full/rust/src/apis/runner_configs_refresh_metadata_api.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/apis/runner_configs_serverless_health_check_api.rs b/engine/sdks/rust/api-full/rust/src/apis/runner_configs_serverless_health_check_api.rs index 41414b64f3..f57436acf6 100644 --- a/engine/sdks/rust/api-full/rust/src/apis/runner_configs_serverless_health_check_api.rs +++ b/engine/sdks/rust/api-full/rust/src/apis/runner_configs_serverless_health_check_api.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/apis/runner_configs_upsert_api.rs b/engine/sdks/rust/api-full/rust/src/apis/runner_configs_upsert_api.rs index c55417426b..be080bcb6e 100644 --- a/engine/sdks/rust/api-full/rust/src/apis/runner_configs_upsert_api.rs +++ b/engine/sdks/rust/api-full/rust/src/apis/runner_configs_upsert_api.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/apis/runners_api.rs b/engine/sdks/rust/api-full/rust/src/apis/runners_api.rs index 184f0cfe3d..410b01febf 100644 --- a/engine/sdks/rust/api-full/rust/src/apis/runners_api.rs +++ b/engine/sdks/rust/api-full/rust/src/apis/runners_api.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ @@ -30,11 +30,12 @@ pub enum RunnersListNamesError { } -pub async fn runners_list(configuration: &configuration::Configuration, namespace: &str, name: Option<&str>, runner_ids: Option<&str>, include_stopped: Option, limit: Option, cursor: Option<&str>) -> Result> { +pub async fn runners_list(configuration: &configuration::Configuration, namespace: &str, name: Option<&str>, runner_ids: Option<&str>, runner_id: Option>, include_stopped: Option, limit: Option, cursor: Option<&str>) -> Result> { // add a prefix to parameters to efficiently prevent name collisions let p_namespace = namespace; let p_name = name; let p_runner_ids = runner_ids; + let p_runner_id = runner_id; let p_include_stopped = include_stopped; let p_limit = limit; let p_cursor = cursor; @@ -49,6 +50,12 @@ pub async fn runners_list(configuration: &configuration::Configuration, namespac if let Some(ref param_value) = p_runner_ids { req_builder = req_builder.query(&[("runner_ids", ¶m_value.to_string())]); } + if let Some(ref param_value) = p_runner_id { + req_builder = match "multi" { + "multi" => req_builder.query(¶m_value.into_iter().map(|p| ("runner_id".to_owned(), p.to_string())).collect::>()), + _ => req_builder.query(&[("runner_id", ¶m_value.into_iter().map(|p| p.to_string()).collect::>().join(",").to_string())]), + }; + } if let Some(ref param_value) = p_include_stopped { req_builder = req_builder.query(&[("include_stopped", ¶m_value.to_string())]); } diff --git a/engine/sdks/rust/api-full/rust/src/models/actor.rs b/engine/sdks/rust/api-full/rust/src/models/actor.rs index f85166b67f..ae810675fb 100644 --- a/engine/sdks/rust/api-full/rust/src/models/actor.rs +++ b/engine/sdks/rust/api-full/rust/src/models/actor.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/actor_name.rs b/engine/sdks/rust/api-full/rust/src/models/actor_name.rs index e6d0d701b6..ec5fcc393a 100644 --- a/engine/sdks/rust/api-full/rust/src/models/actor_name.rs +++ b/engine/sdks/rust/api-full/rust/src/models/actor_name.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/actors_create_request.rs b/engine/sdks/rust/api-full/rust/src/models/actors_create_request.rs index bd1db9240e..5a9da78895 100644 --- a/engine/sdks/rust/api-full/rust/src/models/actors_create_request.rs +++ b/engine/sdks/rust/api-full/rust/src/models/actors_create_request.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/actors_create_response.rs b/engine/sdks/rust/api-full/rust/src/models/actors_create_response.rs index bbedb9f208..4338392488 100644 --- a/engine/sdks/rust/api-full/rust/src/models/actors_create_response.rs +++ b/engine/sdks/rust/api-full/rust/src/models/actors_create_response.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/actors_get_or_create_request.rs b/engine/sdks/rust/api-full/rust/src/models/actors_get_or_create_request.rs index 73aba7b554..d1d2cc26f7 100644 --- a/engine/sdks/rust/api-full/rust/src/models/actors_get_or_create_request.rs +++ b/engine/sdks/rust/api-full/rust/src/models/actors_get_or_create_request.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/actors_get_or_create_response.rs b/engine/sdks/rust/api-full/rust/src/models/actors_get_or_create_response.rs index 3cd95d4d56..f13e444d51 100644 --- a/engine/sdks/rust/api-full/rust/src/models/actors_get_or_create_response.rs +++ b/engine/sdks/rust/api-full/rust/src/models/actors_get_or_create_response.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/actors_kv_get_response.rs b/engine/sdks/rust/api-full/rust/src/models/actors_kv_get_response.rs index 9133891fe9..9ae5c6efb5 100644 --- a/engine/sdks/rust/api-full/rust/src/models/actors_kv_get_response.rs +++ b/engine/sdks/rust/api-full/rust/src/models/actors_kv_get_response.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/actors_list_names_response.rs b/engine/sdks/rust/api-full/rust/src/models/actors_list_names_response.rs index a527518474..5052ccc35e 100644 --- a/engine/sdks/rust/api-full/rust/src/models/actors_list_names_response.rs +++ b/engine/sdks/rust/api-full/rust/src/models/actors_list_names_response.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/actors_list_response.rs b/engine/sdks/rust/api-full/rust/src/models/actors_list_response.rs index fe047eb612..6f069b28f3 100644 --- a/engine/sdks/rust/api-full/rust/src/models/actors_list_response.rs +++ b/engine/sdks/rust/api-full/rust/src/models/actors_list_response.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/crash_policy.rs b/engine/sdks/rust/api-full/rust/src/models/crash_policy.rs index 3333cc10af..8adce99e23 100644 --- a/engine/sdks/rust/api-full/rust/src/models/crash_policy.rs +++ b/engine/sdks/rust/api-full/rust/src/models/crash_policy.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/datacenter.rs b/engine/sdks/rust/api-full/rust/src/models/datacenter.rs index b8ff7585b8..aaf554f1ae 100644 --- a/engine/sdks/rust/api-full/rust/src/models/datacenter.rs +++ b/engine/sdks/rust/api-full/rust/src/models/datacenter.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/datacenter_health.rs b/engine/sdks/rust/api-full/rust/src/models/datacenter_health.rs index 16a757b7b3..bb7bdd5363 100644 --- a/engine/sdks/rust/api-full/rust/src/models/datacenter_health.rs +++ b/engine/sdks/rust/api-full/rust/src/models/datacenter_health.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/datacenters_list_response.rs b/engine/sdks/rust/api-full/rust/src/models/datacenters_list_response.rs index 3600270a9f..358c38093f 100644 --- a/engine/sdks/rust/api-full/rust/src/models/datacenters_list_response.rs +++ b/engine/sdks/rust/api-full/rust/src/models/datacenters_list_response.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/health_fanout_response.rs b/engine/sdks/rust/api-full/rust/src/models/health_fanout_response.rs index ec479fa0b8..c556816154 100644 --- a/engine/sdks/rust/api-full/rust/src/models/health_fanout_response.rs +++ b/engine/sdks/rust/api-full/rust/src/models/health_fanout_response.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/health_response.rs b/engine/sdks/rust/api-full/rust/src/models/health_response.rs index acfe5b3b08..2467df8c37 100644 --- a/engine/sdks/rust/api-full/rust/src/models/health_response.rs +++ b/engine/sdks/rust/api-full/rust/src/models/health_response.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/health_status.rs b/engine/sdks/rust/api-full/rust/src/models/health_status.rs index c10030f8ef..f71627908c 100644 --- a/engine/sdks/rust/api-full/rust/src/models/health_status.rs +++ b/engine/sdks/rust/api-full/rust/src/models/health_status.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/metadata_get_response.rs b/engine/sdks/rust/api-full/rust/src/models/metadata_get_response.rs index d6d734584d..b9eeb9a0b1 100644 --- a/engine/sdks/rust/api-full/rust/src/models/metadata_get_response.rs +++ b/engine/sdks/rust/api-full/rust/src/models/metadata_get_response.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/namespace.rs b/engine/sdks/rust/api-full/rust/src/models/namespace.rs index 8e819d38b3..f6c43a21f6 100644 --- a/engine/sdks/rust/api-full/rust/src/models/namespace.rs +++ b/engine/sdks/rust/api-full/rust/src/models/namespace.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/namespace_list_response.rs b/engine/sdks/rust/api-full/rust/src/models/namespace_list_response.rs index 5b6f0cd660..e83f90f8b2 100644 --- a/engine/sdks/rust/api-full/rust/src/models/namespace_list_response.rs +++ b/engine/sdks/rust/api-full/rust/src/models/namespace_list_response.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/namespaces_create_request.rs b/engine/sdks/rust/api-full/rust/src/models/namespaces_create_request.rs index 62991fe9df..f9dc46da4e 100644 --- a/engine/sdks/rust/api-full/rust/src/models/namespaces_create_request.rs +++ b/engine/sdks/rust/api-full/rust/src/models/namespaces_create_request.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/namespaces_create_response.rs b/engine/sdks/rust/api-full/rust/src/models/namespaces_create_response.rs index bf4d609a21..b23ee02ad9 100644 --- a/engine/sdks/rust/api-full/rust/src/models/namespaces_create_response.rs +++ b/engine/sdks/rust/api-full/rust/src/models/namespaces_create_response.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/pagination.rs b/engine/sdks/rust/api-full/rust/src/models/pagination.rs index 2cdc0d6696..a0e815db55 100644 --- a/engine/sdks/rust/api-full/rust/src/models/pagination.rs +++ b/engine/sdks/rust/api-full/rust/src/models/pagination.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner.rs b/engine/sdks/rust/api-full/rust/src/models/runner.rs index 8c4cbceb1f..0944727a75 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_config.rs b/engine/sdks/rust/api-full/rust/src/models/runner_config.rs index f8e6a09cad..b0bb48b81f 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_config.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_config.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_config_kind.rs b/engine/sdks/rust/api-full/rust/src/models/runner_config_kind.rs index 3bdf38d191..4faa4f90b4 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_config_kind.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_config_kind.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_config_kind_one_of.rs b/engine/sdks/rust/api-full/rust/src/models/runner_config_kind_one_of.rs index eeff6665ff..67d7393580 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_config_kind_one_of.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_config_kind_one_of.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_config_kind_one_of_1.rs b/engine/sdks/rust/api-full/rust/src/models/runner_config_kind_one_of_1.rs index 864220ed9c..ee1b0056a4 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_config_kind_one_of_1.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_config_kind_one_of_1.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_config_kind_one_of_1_serverless.rs b/engine/sdks/rust/api-full/rust/src/models/runner_config_kind_one_of_1_serverless.rs index 91a3de59d9..9e45e75d70 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_config_kind_one_of_1_serverless.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_config_kind_one_of_1_serverless.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_config_variant.rs b/engine/sdks/rust/api-full/rust/src/models/runner_config_variant.rs index dab2ccc500..e2f0c6a962 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_config_variant.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_config_variant.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_configs_list_response.rs b/engine/sdks/rust/api-full/rust/src/models/runner_configs_list_response.rs index a8d304673b..fb1cc7708d 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_configs_list_response.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_configs_list_response.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_configs_list_response_runner_configs_value.rs b/engine/sdks/rust/api-full/rust/src/models/runner_configs_list_response_runner_configs_value.rs index 17cff07328..7fb9693c6f 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_configs_list_response_runner_configs_value.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_configs_list_response_runner_configs_value.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_request.rs b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_request.rs index 05d6c66ee9..afe0a37ec5 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_request.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_request.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_response.rs b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_response.rs index 02633bad8c..c89c01ab7f 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_response.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_response.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_response_one_of.rs b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_response_one_of.rs index db9f1b2d7d..4543372f1d 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_response_one_of.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_response_one_of.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_response_one_of_1.rs b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_response_one_of_1.rs index 7911ecf786..b69358a5d3 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_response_one_of_1.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_response_one_of_1.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_response_one_of_1_failure.rs b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_response_one_of_1_failure.rs index 0b0adc9f2e..f409cd09a4 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_response_one_of_1_failure.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_response_one_of_1_failure.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_response_one_of_success.rs b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_response_one_of_success.rs index 9f487aea8f..edce0b18d9 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_response_one_of_success.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_health_check_response_one_of_success.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error.rs b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error.rs index 110294adb9..80d3798fec 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of.rs b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of.rs index 2d11b277ab..e791fb9fb3 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_1.rs b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_1.rs index acdc31ec2f..4cc8567604 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_1.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_1.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_2.rs b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_2.rs index 1d815bc3b7..78c8239f9a 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_2.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_2.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_3.rs b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_3.rs index 89296489c0..db552cc5fc 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_3.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_3.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_3_non_success_status.rs b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_3_non_success_status.rs index 257a4abd69..7b24ddea53 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_3_non_success_status.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_3_non_success_status.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_4.rs b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_4.rs index c420fa8ac3..b7751bae37 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_4.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_4.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_4_invalid_response_json.rs b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_4_invalid_response_json.rs index d8832b86f3..7baa1280fc 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_4_invalid_response_json.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_4_invalid_response_json.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_5.rs b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_5.rs index 709e983fbb..2349757aa3 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_5.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_5.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_5_invalid_response_schema.rs b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_5_invalid_response_schema.rs index d87f81f63c..1c3e1c3dbc 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_5_invalid_response_schema.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_configs_serverless_metadata_error_one_of_5_invalid_response_schema.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_configs_upsert_request_body.rs b/engine/sdks/rust/api-full/rust/src/models/runner_configs_upsert_request_body.rs index 6e2bd4bbbc..69fe01a6e6 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_configs_upsert_request_body.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_configs_upsert_request_body.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runner_configs_upsert_response.rs b/engine/sdks/rust/api-full/rust/src/models/runner_configs_upsert_response.rs index 05d68a71b2..1f868cc666 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runner_configs_upsert_response.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runner_configs_upsert_response.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runners_list_names_response.rs b/engine/sdks/rust/api-full/rust/src/models/runners_list_names_response.rs index 5a68e99bb2..9db38ea306 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runners_list_names_response.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runners_list_names_response.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/rust/api-full/rust/src/models/runners_list_response.rs b/engine/sdks/rust/api-full/rust/src/models/runners_list_response.rs index 49833c8ed1..cdcea05a88 100644 --- a/engine/sdks/rust/api-full/rust/src/models/runners_list_response.rs +++ b/engine/sdks/rust/api-full/rust/src/models/runners_list_response.rs @@ -3,7 +3,7 @@ * * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * - * The version of the OpenAPI document: 2.0.25-rc.1 + * The version of the OpenAPI document: 2.0.25 * Contact: developer@rivet.gg * Generated by: https://openapi-generator.tech */ diff --git a/engine/sdks/typescript/api-full/src/Client.ts b/engine/sdks/typescript/api-full/src/Client.ts index 61227b5afb..db947e82b8 100644 --- a/engine/sdks/typescript/api-full/src/Client.ts +++ b/engine/sdks/typescript/api-full/src/Client.ts @@ -4,8 +4,8 @@ import * as core from "./core"; import * as Rivet from "./api/index"; -import urlJoin from "url-join"; import * as serializers from "./serialization/index"; +import urlJoin from "url-join"; import * as errors from "./errors/index"; import { Datacenters } from "./api/resources/datacenters/client/Client"; import { Health } from "./api/resources/health/client/Client"; @@ -99,7 +99,7 @@ export class RivetClient { request: Rivet.ActorsListRequest, requestOptions?: RivetClient.RequestOptions, ): Promise { - const { namespace, name, key, actorIds, includeDestroyed, limit, cursor } = request; + const { namespace, name, key, actorIds, actorId, includeDestroyed, limit, cursor } = request; const _queryParams: Record = {}; _queryParams["namespace"] = namespace; if (name != null) { @@ -114,6 +114,16 @@ export class RivetClient { _queryParams["actor_ids"] = actorIds; } + if (actorId != null) { + if (Array.isArray(actorId)) { + _queryParams["actor_id"] = actorId.map((item) => + serializers.RivetId.jsonOrThrow(item, { unrecognizedObjectKeys: "strip" }), + ); + } else { + _queryParams["actor_id"] = actorId; + } + } + if (includeDestroyed != null) { _queryParams["include_destroyed"] = includeDestroyed.toString(); } @@ -608,7 +618,7 @@ export class RivetClient { request: Rivet.RunnerConfigsListRequest, requestOptions?: RivetClient.RequestOptions, ): Promise { - const { namespace, limit, cursor, variant, runnerNames } = request; + const { namespace, limit, cursor, variant, runnerNames, runnerName } = request; const _queryParams: Record = {}; _queryParams["namespace"] = namespace; if (limit != null) { @@ -629,6 +639,14 @@ export class RivetClient { _queryParams["runner_names"] = runnerNames; } + if (runnerName != null) { + if (Array.isArray(runnerName)) { + _queryParams["runner_name"] = runnerName.map((item) => item); + } else { + _queryParams["runner_name"] = runnerName; + } + } + const _response = await (this._options.fetcher ?? core.fetcher)({ url: urlJoin( (await core.Supplier.get(this._options.baseUrl)) ?? diff --git a/engine/sdks/typescript/api-full/src/api/client/requests/ActorsListRequest.ts b/engine/sdks/typescript/api-full/src/api/client/requests/ActorsListRequest.ts index bbf6145de7..1763fa0468 100644 --- a/engine/sdks/typescript/api-full/src/api/client/requests/ActorsListRequest.ts +++ b/engine/sdks/typescript/api-full/src/api/client/requests/ActorsListRequest.ts @@ -2,6 +2,8 @@ * This file was auto-generated by Fern from our API Definition. */ +import * as Rivet from "../../index"; + /** * @example * { @@ -12,7 +14,11 @@ export interface ActorsListRequest { namespace: string; name?: string; key?: string; + /** + * Deprecated. + */ actorIds?: string; + actorId?: Rivet.RivetId | Rivet.RivetId[]; includeDestroyed?: boolean; limit?: number; cursor?: string; diff --git a/engine/sdks/typescript/api-full/src/api/client/requests/RunnerConfigsListRequest.ts b/engine/sdks/typescript/api-full/src/api/client/requests/RunnerConfigsListRequest.ts index cb1e154eb8..870f056e51 100644 --- a/engine/sdks/typescript/api-full/src/api/client/requests/RunnerConfigsListRequest.ts +++ b/engine/sdks/typescript/api-full/src/api/client/requests/RunnerConfigsListRequest.ts @@ -15,5 +15,9 @@ export interface RunnerConfigsListRequest { limit?: number; cursor?: string; variant?: Rivet.RunnerConfigVariant; + /** + * Deprecated. + */ runnerNames?: string; + runnerName?: string | string[]; } diff --git a/engine/sdks/typescript/api-full/src/api/resources/namespaces/client/Client.ts b/engine/sdks/typescript/api-full/src/api/resources/namespaces/client/Client.ts index 99f5e558bc..2d4234f1c8 100644 --- a/engine/sdks/typescript/api-full/src/api/resources/namespaces/client/Client.ts +++ b/engine/sdks/typescript/api-full/src/api/resources/namespaces/client/Client.ts @@ -4,8 +4,8 @@ import * as core from "../../../../core"; import * as Rivet from "../../../index"; -import urlJoin from "url-join"; import * as serializers from "../../../../serialization/index"; +import urlJoin from "url-join"; import * as errors from "../../../../errors/index"; export declare namespace Namespaces { @@ -43,7 +43,7 @@ export class Namespaces { request: Rivet.NamespacesListRequest = {}, requestOptions?: Namespaces.RequestOptions, ): Promise { - const { limit, cursor, name, namespaceIds } = request; + const { limit, cursor, name, namespaceIds, namespaceId } = request; const _queryParams: Record = {}; if (limit != null) { _queryParams["limit"] = limit.toString(); @@ -61,6 +61,16 @@ export class Namespaces { _queryParams["namespace_ids"] = namespaceIds; } + if (namespaceId != null) { + if (Array.isArray(namespaceId)) { + _queryParams["namespace_id"] = namespaceId.map((item) => + serializers.RivetId.jsonOrThrow(item, { unrecognizedObjectKeys: "strip" }), + ); + } else { + _queryParams["namespace_id"] = namespaceId; + } + } + const _response = await (this._options.fetcher ?? core.fetcher)({ url: urlJoin( (await core.Supplier.get(this._options.baseUrl)) ?? diff --git a/engine/sdks/typescript/api-full/src/api/resources/namespaces/client/requests/NamespacesListRequest.ts b/engine/sdks/typescript/api-full/src/api/resources/namespaces/client/requests/NamespacesListRequest.ts index c05f5d60a9..467da1faa2 100644 --- a/engine/sdks/typescript/api-full/src/api/resources/namespaces/client/requests/NamespacesListRequest.ts +++ b/engine/sdks/typescript/api-full/src/api/resources/namespaces/client/requests/NamespacesListRequest.ts @@ -2,6 +2,8 @@ * This file was auto-generated by Fern from our API Definition. */ +import * as Rivet from "../../../../index"; + /** * @example * {} @@ -10,5 +12,9 @@ export interface NamespacesListRequest { limit?: number; cursor?: string; name?: string; + /** + * Deprecated. + */ namespaceIds?: string; + namespaceId?: Rivet.RivetId | Rivet.RivetId[]; } diff --git a/engine/sdks/typescript/api-full/src/api/resources/runners/client/Client.ts b/engine/sdks/typescript/api-full/src/api/resources/runners/client/Client.ts index 770de7d8b0..5fd7ebf056 100644 --- a/engine/sdks/typescript/api-full/src/api/resources/runners/client/Client.ts +++ b/engine/sdks/typescript/api-full/src/api/resources/runners/client/Client.ts @@ -4,8 +4,8 @@ import * as core from "../../../../core"; import * as Rivet from "../../../index"; -import urlJoin from "url-join"; import * as serializers from "../../../../serialization/index"; +import urlJoin from "url-join"; import * as errors from "../../../../errors/index"; export declare namespace Runners { @@ -45,7 +45,7 @@ export class Runners { request: Rivet.RunnersListRequest, requestOptions?: Runners.RequestOptions, ): Promise { - const { namespace, name, runnerIds, includeStopped, limit, cursor } = request; + const { namespace, name, runnerIds, runnerId, includeStopped, limit, cursor } = request; const _queryParams: Record = {}; _queryParams["namespace"] = namespace; if (name != null) { @@ -56,6 +56,16 @@ export class Runners { _queryParams["runner_ids"] = runnerIds; } + if (runnerId != null) { + if (Array.isArray(runnerId)) { + _queryParams["runner_id"] = runnerId.map((item) => + serializers.RivetId.jsonOrThrow(item, { unrecognizedObjectKeys: "strip" }), + ); + } else { + _queryParams["runner_id"] = runnerId; + } + } + if (includeStopped != null) { _queryParams["include_stopped"] = includeStopped.toString(); } diff --git a/engine/sdks/typescript/api-full/src/api/resources/runners/client/requests/RunnersListRequest.ts b/engine/sdks/typescript/api-full/src/api/resources/runners/client/requests/RunnersListRequest.ts index c8acfe7fe0..65cd5e0795 100644 --- a/engine/sdks/typescript/api-full/src/api/resources/runners/client/requests/RunnersListRequest.ts +++ b/engine/sdks/typescript/api-full/src/api/resources/runners/client/requests/RunnersListRequest.ts @@ -2,6 +2,8 @@ * This file was auto-generated by Fern from our API Definition. */ +import * as Rivet from "../../../../index"; + /** * @example * { @@ -11,7 +13,11 @@ export interface RunnersListRequest { namespace: string; name?: string; + /** + * Deprecated. + */ runnerIds?: string; + runnerId?: Rivet.RivetId | Rivet.RivetId[]; includeStopped?: boolean; limit?: number; cursor?: string; diff --git a/engine/sdks/typescript/runner/src/tunnel.ts b/engine/sdks/typescript/runner/src/tunnel.ts index eba4253d1f..6b4115c4a3 100644 --- a/engine/sdks/typescript/runner/src/tunnel.ts +++ b/engine/sdks/typescript/runner/src/tunnel.ts @@ -10,7 +10,7 @@ import { stringify as uuidstringify, v4 as uuidv4, } from "uuid"; -import { RunnerShutdownError, type Runner, type RunnerActor } from "./mod"; +import { type Runner, type RunnerActor, RunnerShutdownError } from "./mod"; import { stringifyToClientTunnelMessageKind, stringifyToServerTunnelMessageKind, diff --git a/frontend/package.json b/frontend/package.json index 633ef73254..c39584e6c5 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -67,6 +67,7 @@ "@stepperize/react": "^5.1.8", "@tailwindcss/container-queries": "^0.1.1", "@tailwindcss/typography": "^0.5.16", + "@tanstack/history": "^1.133.28", "@tanstack/query-core": "^5.87.1", "@tanstack/react-query": "^5.87.1", "@tanstack/react-query-devtools": "^5.87.3", @@ -88,6 +89,7 @@ "@types/node": "^20.19.13", "@types/react": "^19", "@types/react-dom": "^19", + "@types/reconnectingwebsocket": "^1.0.10", "@uiw/codemirror-extensions-basic-setup": "^4.25.1", "@uiw/codemirror-theme-github": "^4.25.1", "@uiw/react-codemirror": "^4.25.1", @@ -96,6 +98,7 @@ "autoprefixer": "^10.4.21", "bcryptjs": "^2.4.3", "canvas-confetti": "^1.9.3", + "cbor-x": "^1.6.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", @@ -120,6 +123,7 @@ "react-inspector": "^6.0.2", "react-resizable-panels": "^2.1.9", "recharts": "^2.15.4", + "reconnectingwebsocket": "^1.0.0", "rivetkit": "workspace:*", "shiki": "^3.12.2", "sonner": "^1.7.4", diff --git a/frontend/packages/components/src/actors/actor-build.tsx b/frontend/packages/components/src/actors/actor-build.tsx deleted file mode 100644 index 8416f0dd4e..0000000000 --- a/frontend/packages/components/src/actors/actor-build.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { Dd, DiscreteCopyButton, Dl, Dt, Flex } from "@rivet-gg/components"; -import { formatISO } from "date-fns"; -import { useAtomValue } from "jotai"; -import { selectAtom } from "jotai/utils"; -import { useCallback } from "react"; -import { type Actor, type ActorAtom, actorBuildsAtom } from "./actor-context"; -import { ActorTags } from "./actor-tags"; - -const buildIdSelector = (a: Actor) => a.runtime?.build; - -interface ActorBuildProps { - actor: ActorAtom; -} - -export function ActorBuild({ actor }: ActorBuildProps) { - const buildId = useAtomValue(selectAtom(actor, buildIdSelector)); - - const data = useAtomValue( - selectAtom( - actorBuildsAtom, - useCallback( - (builds) => { - return builds.find((build) => build.id === buildId); - }, - [buildId], - ), - ), - ); - - if (!data) { - return null; - } - - return ( -
-
-

Build

-
- -
-
ID
-
- - {data.id} - -
-
Created
-
- - {formatISO(data.createdAt)} - -
-
Tags
-
- - - -
-
-
-
- ); -} diff --git a/frontend/packages/components/src/actors/actor-config-tab.tsx b/frontend/packages/components/src/actors/actor-config-tab.tsx deleted file mode 100644 index 2a05406888..0000000000 --- a/frontend/packages/components/src/actors/actor-config-tab.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { Button, DocsSheet, ScrollArea } from "@rivet-gg/components"; -import { Icon, faBooks } from "@rivet-gg/icons"; -import type { ActorAtom } from "./actor-context"; -import { ActorGeneral } from "./actor-general"; -import { ActorNetwork } from "./actor-network"; -import { ActorRuntime } from "./actor-runtime"; - -interface ActorConfigTabProps { - actor: ActorAtom; -} - -export function ActorConfigTab(props: ActorConfigTabProps) { - return ( - -
- - - -
- - - -
- ); -} diff --git a/frontend/packages/components/src/actors/actor-connections-tab.tsx b/frontend/packages/components/src/actors/actor-connections-tab.tsx deleted file mode 100644 index 42be046a7c..0000000000 --- a/frontend/packages/components/src/actors/actor-connections-tab.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { LiveBadge, ScrollArea } from "@rivet-gg/components"; -import { useAtomValue } from "jotai"; -import { selectAtom } from "jotai/utils"; -import type { Actor, ActorAtom } from "./actor-context"; -import { ActorObjectInspector } from "./console/actor-inspector"; -import { - useActorConnections, - useActorWorkerStatus, -} from "./worker/actor-worker-context"; - -const selector = (a: Actor) => a.destroyedAt; - -interface ActorConnectionsTabProps { - actor: ActorAtom; -} - -export function ActorConnectionsTab({ actor }: ActorConnectionsTabProps) { - const destroyedAt = useAtomValue(selectAtom(actor, selector)); - const status = useActorWorkerStatus(); - - const connections = useActorConnections(); - - if (destroyedAt) { - return ( -
- Connections Preview is unavailable for inactive Actors. -
- ); - } - - if (status.type === "error") { - return ( -
- Connections Preview is currently unavailable. -
- See console/logs for more details. -
- ); - } - - if (status.type !== "ready") { - return ( -
- Loading connections... -
- ); - } - - return ( - -
- -
-
- [c.id, c]))} - expandPaths={["$"]} - /> -
-
- ); -} diff --git a/frontend/packages/components/src/actors/actor-context.tsx b/frontend/packages/components/src/actors/actor-context.tsx deleted file mode 100644 index 7aab38c8fa..0000000000 --- a/frontend/packages/components/src/actors/actor-context.tsx +++ /dev/null @@ -1,437 +0,0 @@ -import type { Rivet } from "@rivet-gg/api"; -import { isAfter, isBefore } from "date-fns"; -import { type Atom, atom } from "jotai"; -import { atomFamily, splitAtom } from "jotai/utils"; -import { toRecord } from "../lib/utils"; -import { FilterOp, type FilterValue } from "../ui/filters"; -import { ACTOR_FRAMEWORK_TAG_VALUE } from "./actor-tags"; - -export enum ActorFeature { - Logs = "logs", - Config = "config", - Connections = "connections", - State = "state", - Console = "console", - Runtime = "runtime", - Metrics = "metrics", - InspectReconnectNotification = "inspect_reconnect_notification", -} - -export type Actor = Omit< - Rivet.actor.Actor, - "createdAt" | "runtime" | "lifecycle" | "network" | "resources" -> & { - status: "unknown" | "starting" | "running" | "stopped" | "crashed"; - - lifecycle?: Rivet.actor.Lifecycle; - endpoint?: string; - logs: LogsAtom; - metrics: MetricsAtom; - network?: Rivet.actor.Network | null; - resources?: Rivet.actor.Resources | null; - runtime?: Rivet.actor.Runtime | null; - destroy?: DestroyActorAtom; - destroyTs?: Date; - createdAt?: Date; - features?: ActorFeature[]; -}; - -export type Logs = { - id: string; - level: "error" | "info"; - timestamp: Date; - line: string; - message: string; - properties: Record; -}[]; - -export type Metrics = Record; - -export type Build = Rivet.actor.Build; -export type DestroyActor = { - isDestroying: boolean; - destroy: () => Promise; -}; - -export type ActorAtom = Atom; -export type LogsAtom = Atom<{ - logs: Logs; - // query status - status: string; -}>; -export type MetricsAtom = Atom<{ - metrics: Metrics; - updatedAt: number; - // query status - status: string; -}>; -export type BuildAtom = Atom; -export type DestroyActorAtom = Atom; - -export type CreateActor = { - create: (values: { - endpoint: string; - id: string; - tags: Record; - region?: string; - params?: Record; - }) => Promise; - isCreating: boolean; - endpoint: string | null; -}; - -export type Region = Rivet.actor.Region; - -// global atoms -export const currentActorIdAtom = atom(undefined); - -export const currentActorQueryAtom = atom<{ - isLoading: boolean; - error: string | null; -}>({ - isLoading: false, - error: null, -}); -export const actorsQueryAtom = atom<{ - isLoading: boolean; - error: string | null; -}>({ - isLoading: false, - error: null, -}); -export const actorsAtom = atom([]); -export const actorFiltersAtom = atom<{ - tags: FilterValue; - region: FilterValue; - createdAt: FilterValue; - destroyedAt: FilterValue; - status: FilterValue; - devMode: FilterValue; -}>({ - tags: undefined, - region: undefined, - createdAt: undefined, - destroyedAt: undefined, - status: undefined, - devMode: undefined, -}); -export const actorsPaginationAtom = atom({ - hasNextPage: false, - isFetchingNextPage: false, - fetchNextPage: () => {}, -}); - -export const actorRegionsAtom = atom([ - { - id: "default", - name: "Default", - }, -]); - -export const actorBuildsAtom = atom([]); - -export const actorEnvironmentAtom = atom<{ - projectNameId: string; - environmentNameId: string; -} | null>(null); - -export const actorMetricsTimeWindowAtom = atom(15 * 60 * 1000); // Default to 15 minutes - -export const actorsInternalFilterAtom = atom<{ - fn: (actor: Actor) => boolean; -}>(); - -// derived atoms - -export const currentActorRegionAtom = atom((get) => { - const actorAtom = get(currentActorAtom); - if (!actorAtom) { - return undefined; - } - const regions = get(actorRegionsAtom); - const actor = get(actorAtom); - return regions.find((region) => region.id === actor.region); -}); -export const filteredActorsAtom = atom((get) => { - const filters = get(actorFiltersAtom); - const actors = get(actorsAtom); - - const isActorInternal = get(actorsInternalFilterAtom)?.fn; - - return actors.filter((actor) => { - const satisfiesFilters = Object.entries(filters).every( - ([key, filter]) => { - if (filter === undefined) { - return true; - } - if (key === "tags") { - const filterTags = filter.value.map((tag) => - tag.split("="), - ); - const tags = toRecord(actor.tags); - - if (filter.operator === FilterOp.NOT_EQUAL) { - return Object.entries(tags).every( - ([tagKey, tagValue]) => { - return filterTags.every( - ([filterKey, filterValue]) => { - if (filterKey === tagKey) { - if (filterValue === "*") { - return false; - } - return tagValue !== filterValue; - } - return true; - }, - ); - }, - ); - } - - return Object.entries(tags).some(([tagKey, tagValue]) => { - return filterTags.some(([filterKey, filterValue]) => { - if (filterKey === tagKey) { - if (filterValue === "*") { - return true; - } - return tagValue === filterValue; - } - return false; - }); - }); - } - - if (key === "region") { - if (filter.operator === FilterOp.NOT_EQUAL) { - return !filter.value.includes(actor.region); - } - - return filter.value.includes(actor.region); - } - - if (key === "createdAt") { - if (actor.createdAt === undefined) { - return false; - } - const createdAt = new Date(actor.createdAt); - - if (filter.operator === FilterOp.AFTER) { - return isAfter(createdAt, +filter.value[0]); - } - if (filter.operator === FilterOp.BEFORE) { - return isBefore(createdAt, +filter.value[0]); - } - if (filter.operator === FilterOp.BETWEEN) { - return ( - isAfter(createdAt, +filter.value[0]) && - isBefore(createdAt, +filter.value[1]) - ); - } - return false; - } - - if (key === "destroyedAt") { - if (actor.destroyTs === undefined) { - return false; - } - const destroyedAt = new Date(actor.destroyTs); - - if (filter.operator === FilterOp.AFTER) { - return isAfter(destroyedAt, +filter.value[0]); - } - if (filter.operator === FilterOp.BEFORE) { - return isBefore(destroyedAt, +filter.value[0]); - } - if (filter.operator === FilterOp.BETWEEN) { - return ( - isAfter(destroyedAt, +filter.value[0]) && - isBefore(destroyedAt, +filter.value[1]) - ); - } - return false; - } - - if (key === "status") { - if (filter.operator === FilterOp.NOT_EQUAL) { - return !filter.value.includes(actor.status); - } - - return filter.value.includes(actor.status); - } - - return true; - }, - ); - - const isInternal = - toRecord(actor.tags).owner === "rivet" || - (isActorInternal?.(actor) ?? false); - - return ( - satisfiesFilters && ((isInternal && filters.devMode) || !isInternal) - ); - }); -}); -export const actorsAtomsAtom = splitAtom( - filteredActorsAtom, - (actor) => actor.id, -); -export const actorsCountAtom = atom((get) => get(actorsAtom).length); -export const filteredActorsCountAtom = atom( - (get) => get(filteredActorsAtom).length, -); - -export const currentActorAtom = atom((get) => { - const actorId = get(currentActorIdAtom); - return get(actorsAtomsAtom).find((actor) => get(actor).id === actorId); -}); - -export const isCurrentActorAtom = atomFamily((actor: ActorAtom) => - atom((get) => { - const actorId = get(currentActorIdAtom); - return get(actor).id === actorId; - }), -); - -export const actorFiltersCountAtom = atom((get) => { - const filters = get(actorFiltersAtom); - return Object.values(filters).filter((value) => value !== undefined).length; -}); - -// tags created by the user, not from the server -export const actorCustomTagValues = atom([]); -export const actorCustomTagKeys = atom([]); - -const actorCustomTagsAtom = atom<{ keys: string[]; values: string[] }>( - (get) => { - const keys = get(actorCustomTagKeys); - const values = get(actorCustomTagValues); - - return { keys, values }; - }, - // @ts-expect-error - (get, set, value: { key: string; value: string }) => { - set(actorCustomTagKeys, (keys) => { - const newKeys = [...keys]; - const index = newKeys.indexOf(value.key); - if (index === -1) { - newKeys.push(value.key); - } - return newKeys; - }); - set(actorCustomTagValues, (values) => { - const newValues = [...values]; - const index = newValues.indexOf(value.value); - if (index === -1) { - newValues.push(value.value); - } - return newValues; - }); - }, -); - -export const createActorAtom = atom({ - endpoint: null, - isCreating: false, - create: async () => {}, -}); - -export const actorManagerEndpointAtom = atom((get) => { - return get(createActorAtom)?.endpoint ?? null; -}); - -export const actorTagsAtom = atom((get) => { - const actorTags = get(actorsAtom).flatMap((actor) => - Object.entries(toRecord(actor.tags)).map(([key, value]) => ({ - key, - value: value as string, - })), - ); - - const keys = new Set(); - const values = new Set(); - - for (const { key, value } of actorTags) { - keys.add(key); - values.add(value); - } - - const customTags = get(actorCustomTagsAtom); - - for (const key of customTags.keys) { - keys.add(key); - } - - for (const value of customTags.values) { - values.add(value); - } - - const allTags = []; - - for (const key of keys) { - for (const value of values) { - allTags.push({ key, value }); - } - } - - return allTags; -}); - -export const actorTagValuesAtom = atom((get) => { - const tags = get(actorTagsAtom); - const values = new Set(); - for (const tag of tags) { - values.add(tag.value); - } - return [...values]; -}); - -export const actorTagKeysAtom = atom((get) => { - const tags = get(actorTagsAtom); - const keys = new Set(); - for (const tag of tags) { - keys.add(tag.key); - } - return [...keys]; -}); - -export const actorBuildsCountAtom = atom((get) => { - return get(actorBuildsAtom).length; -}); - -const commonActorFeatures = [ - ActorFeature.Logs, - ActorFeature.Config, - ActorFeature.Runtime, - // ActorFeature.Metrics, - // ActorFeature.InspectReconnectNotification, -]; - -export const currentActorFeaturesAtom = atom((get) => { - const atom = get(currentActorAtom); - if (!atom) { - return []; - } - - const actor = get(atom); - - // actors from hub - if (!actor.features) { - const tags = toRecord(actor.tags); - if (tags.framework === ACTOR_FRAMEWORK_TAG_VALUE) { - if (tags.name === "manager") { - return commonActorFeatures; - } - return [ - ...commonActorFeatures, - // ActorFeature.Connections, - // ActorFeature.State, - // ActorFeature.Console, - // ActorFeature.InspectReconnectNotification, - ]; - } - return [...commonActorFeatures, ActorFeature.Metrics]; - } - - return actor.features; -}); diff --git a/frontend/packages/components/src/actors/actor-cpu-stats.tsx b/frontend/packages/components/src/actors/actor-cpu-stats.tsx deleted file mode 100644 index 2cf694f339..0000000000 --- a/frontend/packages/components/src/actors/actor-cpu-stats.tsx +++ /dev/null @@ -1,149 +0,0 @@ -import { format } from "date-fns"; -import { useId } from "react"; -import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts"; -import { timing } from "../lib/timing"; -import { - type ChartConfig, - ChartContainer, - ChartTooltip, - ChartTooltipContent, -} from "../ui/chart"; - -interface ActorCpuStatsProps { - interval?: number; - cpu: number[]; - metricsAt: number; - syncId?: string; - isRunning?: boolean; -} - -const chartConfig = { - value: { - color: "hsl(var(--chart-1))", - label: "CPU Usage", - }, -} satisfies ChartConfig; - -export function ActorCpuStats({ - interval = 15, - cpu, - metricsAt, - syncId, - isRunning = true, -}: ActorCpuStatsProps) { - // Filter out trailing zeros in the last 15 seconds only if actor is still running - let filteredCpu = [...cpu]; - if (isRunning) { - const secondsToCheck = 15; - const pointsToCheck = Math.ceil(secondsToCheck / interval); - - // Find the last non-zero value and cut off any zeros after it - for ( - let i = filteredCpu.length - 1; - i >= Math.max(0, filteredCpu.length - pointsToCheck); - i-- - ) { - if (filteredCpu[i] === 0) { - filteredCpu = filteredCpu.slice(0, i); - } else { - break; - } - } - } - - const data = filteredCpu.map((value, i) => { - let cpuPercent = 0; - - // Calculate CPU percentage using delta time between ticks - if (i > 0) { - const currentCpuTime = value; - const previousCpuTime = filteredCpu[i - 1]; - const deltaTime = interval; // seconds between measurements - - // CPU percentage = (cpu_time_delta / time_delta) * 100 - // This gives us the percentage of CPU time used in the interval - if (currentCpuTime >= previousCpuTime) { - cpuPercent = Math.min( - ((currentCpuTime - previousCpuTime) / deltaTime) * 100, - 100, - ); - } - } - - return { - x: `${(filteredCpu.length - i) * -interval}`, - value: cpuPercent / 100, // Convert to 0-1 range for chart - config: { - label: new Date( - metricsAt - - (filteredCpu.length - i) * timing.seconds(interval), - ), - }, - }; - }); - - const id = useId(); - - const fillId = `fill-${id}`; - return ( - - - - - `${value * 100}%`} - /> - { - return format(label, "HH:mm:ss"); - }} - valueFormatter={(value) => { - if (typeof value !== "number") { - return "n/a"; - } - return `${(value * 100).toFixed(2)}%`; - }} - /> - } - /> - - - - - - - - - - ); -} diff --git a/frontend/packages/components/src/actors/actor-details-settings-button.tsx b/frontend/packages/components/src/actors/actor-details-settings-button.tsx deleted file mode 100644 index 9e4684bd40..0000000000 --- a/frontend/packages/components/src/actors/actor-details-settings-button.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { - Button, - DropdownMenu, - DropdownMenuCheckboxItem, - DropdownMenuContent, - DropdownMenuTrigger, - WithTooltip, -} from "@rivet-gg/components"; -import { Icon, faCog } from "@rivet-gg/icons"; -import { useActorDetailsSettings } from "./actor-details-settings"; - -export function ActorDetailsSettingsButton() { - const [settings, setSettings] = useActorDetailsSettings(); - - return ( - - - - - } - content="Settings" - /> - - { - setSettings((old) => ({ - ...old, - showTimestamps: value, - })); - }} - > - Show timestamps - - { - setSettings((old) => ({ - ...old, - autoFollowLogs: value, - })); - }} - > - Auto follow logs when scrolled to bottom - - - - ); -} diff --git a/frontend/packages/components/src/actors/actor-details-settings.tsx b/frontend/packages/components/src/actors/actor-details-settings.tsx deleted file mode 100644 index 622ce862e4..0000000000 --- a/frontend/packages/components/src/actors/actor-details-settings.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { - type Dispatch, - type ReactNode, - type SetStateAction, - createContext, - useContext, -} from "react"; -import { useLocalStorage } from "usehooks-ts"; - -export interface Settings { - showTimestamps: boolean; - autoFollowLogs: boolean; -} - -export const ActorDetailsSettingsContext = createContext< - [Settings, Dispatch>, unknown] ->([{ showTimestamps: false, autoFollowLogs: true }, () => {}, {}]); - -export const useActorDetailsSettings = () => { - const value = useContext(ActorDetailsSettingsContext); - return value; -}; - -interface ActorDetailsSettingsProviderProps { - children: ReactNode; -} - -export const ActorDetailsSettingsProvider = ({ - children, -}: ActorDetailsSettingsProviderProps) => { - const localStorage = useLocalStorage("actor-details-settings", { - showTimestamps: false, - autoFollowLogs: true, - }); - - return ( - - {children} - - ); -}; diff --git a/frontend/packages/components/src/actors/actor-download-logs-button.tsx b/frontend/packages/components/src/actors/actor-download-logs-button.tsx deleted file mode 100644 index a261529c32..0000000000 --- a/frontend/packages/components/src/actors/actor-download-logs-button.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { Button, WithTooltip } from "@rivet-gg/components"; -import { Icon, faSave } from "@rivet-gg/icons"; -import { useAtomValue } from "jotai"; -import type { ActorAtom } from "./actor-context"; -import type { LogsTypeFilter } from "./actor-logs"; - -interface ActorDownloadLogsButtonProps { - actor: ActorAtom; - typeFilter?: LogsTypeFilter; - filter?: string; - onExportLogs?: ( - actorId: string, - typeFilter?: string, - filter?: string, - ) => Promise; - isExporting?: boolean; -} - -export function ActorDownloadLogsButton({ - actor, - typeFilter, - filter, - onExportLogs, - isExporting = false, -}: ActorDownloadLogsButtonProps) { - const actorData = useAtomValue(actor); - - const handleDownload = async () => { - if (!onExportLogs) { - console.warn("No export handler provided"); - return; - } - - try { - await onExportLogs(actorData.id, typeFilter, filter); - } catch (error) { - console.error("Failed to export logs:", error); - } - }; - - return ( - - - - } - /> - ); -} diff --git a/frontend/packages/components/src/actors/actor-editable-state.tsx b/frontend/packages/components/src/actors/actor-editable-state.tsx deleted file mode 100644 index e54e474548..0000000000 --- a/frontend/packages/components/src/actors/actor-editable-state.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import { Badge, Button, WithTooltip } from "@rivet-gg/components"; -import { - type CodeMirrorRef, - EditorView, - JsonCode, -} from "@rivet-gg/components/code-mirror"; -import { Icon, faRotateLeft, faSave } from "@rivet-gg/icons"; -import { AnimatePresence, motion } from "framer-motion"; -import { useMemo, useRef, useState } from "react"; -import { ActorStateChangeIndicator } from "./actor-state-change-indicator"; -import type { ContainerState } from "./worker/actor-worker-container"; -import { useActorWorker } from "./worker/actor-worker-context"; - -const isValidJson = (json: string | null): json is string => { - if (!json) return false; - try { - JSON.parse(json); - return true; - } catch { - return false; - } -}; - -interface ActorEditableStateProps { - state: ContainerState["state"]; -} - -export function ActorEditableState({ state }: ActorEditableStateProps) { - const container = useActorWorker(); - const [isEditing, setIsEditing] = useState(false); - const [value, setValue] = useState(null); - - const ref = useRef(null); - - const formatted = useMemo(() => { - return JSON.stringify(state.value || "{}", null, 2); - }, [state.value]); - - const isValid = isValidJson(value) ? JSON.parse(value) : false; - - return ( - <> -
-
- -
-
- - {isEditing ? ( - - - Modified - - - } - content="State has been modified and not saved." - /> - ) : null} - - { - container.setState(value || ""); - setIsEditing(false); - setValue(null); - }} - > - - - } - /> - { - setValue(null); - setIsEditing(false); - }} - > - - - } - /> -
-
-
- { - setValue(value); - setIsEditing(true); - }} - /> -
- - ); -} diff --git a/frontend/packages/components/src/actors/actor-general.tsx b/frontend/packages/components/src/actors/actor-general.tsx deleted file mode 100644 index b9269fb39e..0000000000 --- a/frontend/packages/components/src/actors/actor-general.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { Dd, DiscreteCopyButton, Dl, Dt, Flex, cn } from "@rivet-gg/components"; -import { formatISO } from "date-fns"; -import equal from "fast-deep-equal"; -import { useAtomValue } from "jotai"; -import { selectAtom } from "jotai/utils"; -import type { Actor, ActorAtom } from "./actor-context"; -import { ActorRegion } from "./actor-region"; -import { ActorTags } from "./actor-tags"; - -const selector = (a: Actor) => ({ - id: a.id, - tags: a.tags, - createdAt: a.createdAt, - destroyedAt: a.destroyedAt, - region: a.region, -}); - -export interface ActorGeneralProps { - actor: ActorAtom; -} - -export function ActorGeneral({ actor }: ActorGeneralProps) { - const { id, tags, createdAt, destroyedAt, region } = useAtomValue( - selectAtom(actor, selector, equal), - ); - - return ( -
-

General

- -
-
Region
-
- -
-
ID
-
- - {id} - -
-
Tags
-
- - - -
-
Created
-
- - {createdAt ? formatISO(createdAt) : "n/a"} - -
-
Destroyed
-
- - {destroyedAt ? formatISO(destroyedAt) : "n/a"} - -
-
-
-
- ); -} diff --git a/frontend/packages/components/src/actors/actor-logs-tab.tsx b/frontend/packages/components/src/actors/actor-logs-tab.tsx deleted file mode 100644 index 7925e94cc0..0000000000 --- a/frontend/packages/components/src/actors/actor-logs-tab.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import { LogsView, ToggleGroup, ToggleGroupItem } from "@rivet-gg/components"; -import { startTransition, useState } from "react"; -import type { ActorAtom } from "./actor-context"; -import { ActorDetailsSettingsButton } from "./actor-details-settings-button"; -import { ActorDownloadLogsButton } from "./actor-download-logs-button"; -import { ActorLogs, type LogsTypeFilter } from "./actor-logs"; - -interface ActorLogsTabProps { - actor: ActorAtom; - onExportLogs?: ( - actorId: string, - typeFilter?: string, - filter?: string, - ) => Promise; - isExporting?: boolean; -} - -export function ActorLogsTab({ - actor, - onExportLogs, - isExporting, -}: ActorLogsTabProps) { - const [search, setSearch] = useState(""); - const [logsFilter, setLogsFilter] = useState("all"); - - return ( -
-
-
-
- - startTransition(() => setSearch(e.target.value)) - } - /> -
- { - if (!value) { - setLogsFilter("all"); - } else { - setLogsFilter(value as LogsTypeFilter); - } - }} - className="gap-0 text-xs p-2 border-r" - > - - all - - - output - - - errors - - - - -
-
-
- -
-
- ); -} - -ActorLogsTab.Skeleton = () => { - return ( -
- -
- ); -}; diff --git a/frontend/packages/components/src/actors/actor-logs.tsx b/frontend/packages/components/src/actors/actor-logs.tsx deleted file mode 100644 index 11427cab70..0000000000 --- a/frontend/packages/components/src/actors/actor-logs.tsx +++ /dev/null @@ -1,219 +0,0 @@ -import { ShimmerLine, VirtualScrollArea } from "@rivet-gg/components"; -import type { Virtualizer } from "@tanstack/react-virtual"; -import { useAtomValue } from "jotai"; -import { selectAtom } from "jotai/utils"; -import { memo, useCallback, useEffect, useRef } from "react"; -import { useResizeObserver } from "usehooks-ts"; -import type { Actor, ActorAtom, Logs } from "./actor-context"; -import { useActorDetailsSettings } from "./actor-details-settings"; -import { ActorConsoleMessage } from "./console/actor-console-message"; - -export type LogsTypeFilter = "all" | "output" | "errors"; - -const selector = (a: Actor) => a.logs; - -interface ActorLogsProps { - actor: ActorAtom; - typeFilter?: LogsTypeFilter; - filter?: string; -} - -export const ActorLogs = memo( - ({ typeFilter, actor, filter }: ActorLogsProps) => { - const [settings] = useActorDetailsSettings(); - const follow = useRef(true); - const shouldFollow = () => settings.autoFollowLogs && follow.current; - - const viewport = useRef(null); - const virtualizer = useRef>(null); - // Detect if the container has resized (i.e, console was opened) - useResizeObserver({ - ref: viewport, - onResize: () => { - if (shouldFollow()) { - // https://github.com/TanStack/virtual/issues/537 - requestAnimationFrame(() => { - virtualizer.current?.scrollToIndex(combined.length, { - align: "end", - }); - }); - } - }, - }); - - const logsAtom = useAtomValue(selectAtom(actor, selector)); - - const { logs, status } = useAtomValue(logsAtom); - - const combined = filterLogs({ - typeFilter: typeFilter ?? "all", - filter: filter ?? "", - logs, - }); - - // Scroll to the bottom when new logs are added - // biome-ignore lint/correctness/useExhaustiveDependencies: run this effect only when the length of the logs changes - useEffect(() => { - if (!shouldFollow()) { - return () => {}; - } - // https://github.com/TanStack/virtual/issues/537 - const rafId = requestAnimationFrame(() => { - virtualizer.current?.scrollToIndex( - virtualizer.current.options.count - 1, - { - align: "end", - }, - ); - }); - - return () => { - cancelAnimationFrame(rafId); - }; - }, [combined.length]); - - // Detect if the user has scrolled all the way to the bottom - const handleChange = useCallback( - (instance: Virtualizer, sync: boolean) => { - if (sync) { - return; - } - - follow.current = - !instance.isScrolling && - instance.range?.endIndex === instance.options.count - 1; - }, - [], - ); - - // if (isStdOutLoading || isStdErrLoading) { - // return ( - //
- // - // Loading logs... - // - //
- // ); - // } - - // const status = getActorStatus({ createdAt, startedAt, destroyedAt }); - - if (status === "starting" && combined.length === 0) { - return ( -
- - [SYSTEM]: Actor is starting... - -
- ); - } - - if (status === "pending") { - return ( - <> - -
- - Loading logs... - -
- - ); - } - - if (combined.length === 0) { - // if (!isStdOutSuccess || !isStdErrSuccess) { - // return ( - //
- // - // [SYSTEM]: Couldn't find the logs. Please try again - // later. - // - //
- // ); - // } - return ( -
- - [SYSTEM]: No logs found. Logs are retained for 3 days. - -
- ); - } - - return ( - <> - - ({ - ...combined[index], - children: - combined[index].message || combined[index].line, - variant: combined[index].level, - timestamp: settings.showTimestamps - ? combined[index].timestamp - : undefined, - })} - onChange={handleChange} - count={combined.length} - estimateSize={() => 26} - row={ActorConsoleMessage} - /> - - ); - }, -); - -interface ScrollerProps { - virtualizer: React.MutableRefObject | null>; -} - -function Scroller({ virtualizer }: ScrollerProps) { - // biome-ignore lint/correctness/useExhaustiveDependencies: scroll on mount, no need to run this effect again - useEffect(() => { - // https://github.com/TanStack/virtual/issues/537 - virtualizer.current?.scrollToIndex( - virtualizer.current.options.count - 1, - { - align: "end", - }, - ); - }, []); - - return null; -} - -export function filterLogs({ - typeFilter, - filter, - logs, -}: { typeFilter: LogsTypeFilter; filter: string; logs: Logs }) { - const output = logs?.filter((log) => { - if (typeFilter === "errors") { - return log.level === "error"; - } - if (typeFilter === "output") { - return log.level !== "error"; - } - return true; - }); - - // Search - const filtered = - filter && filter.trim() !== "" - ? output.filter((log) => log.message.includes(filter)) - : output; - - const sorted = filtered.toSorted( - (a, b) => - new Date(a.timestamp).valueOf() - new Date(b.timestamp).valueOf(), - ); - - return sorted; -} diff --git a/frontend/packages/components/src/actors/actor-memory-stats.tsx b/frontend/packages/components/src/actors/actor-memory-stats.tsx deleted file mode 100644 index f2af24d75d..0000000000 --- a/frontend/packages/components/src/actors/actor-memory-stats.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import { format } from "date-fns"; -import { filesize } from "filesize"; -import { useId } from "react"; -import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts"; -import { timing } from "../lib/timing"; -import { - type ChartConfig, - ChartContainer, - ChartTooltip, - ChartTooltipContent, -} from "../ui/chart"; - -interface ActorMemoryStatsProps { - metricsAt: number; - memory: number[]; - allocatedMemory?: number; - syncId?: string; - interval?: number; - isRunning?: boolean; -} - -const chartConfig = { - value: { - color: "hsl(var(--chart-1))", - label: "Memory Usage", - }, -} satisfies ChartConfig; - -export function ActorMemoryStats({ - interval = 15, - memory, - allocatedMemory, - metricsAt, - syncId, - isRunning = true, -}: ActorMemoryStatsProps) { - // Filter out trailing zeros in the last 15 seconds only if actor is still running - let filteredMemory = [...memory]; - if (isRunning) { - const secondsToCheck = 15; - const pointsToCheck = Math.ceil(secondsToCheck / interval); - - // Find the last non-zero value and cut off any zeros after it - for ( - let i = filteredMemory.length - 1; - i >= Math.max(0, filteredMemory.length - pointsToCheck); - i-- - ) { - if (filteredMemory[i] === 0) { - filteredMemory = filteredMemory.slice(0, i); - } else { - break; - } - } - } - - const data = filteredMemory.map((value, i) => ({ - x: `${(filteredMemory.length - i) * -interval}`, - value, - config: { - label: new Date( - metricsAt - - (filteredMemory.length - i) * timing.seconds(interval), - ), - }, - })); - - const max = allocatedMemory || Math.max(...filteredMemory); - - const id = useId(); - - const fillId = `fill-${id}`; - return ( - - - - - - `${Math.ceil((value / max) * 100)}%` - } - /> - { - return format(label, "HH:mm:ss"); - }} - valueFormatter={(value) => { - if (typeof value !== "number") { - return "n/a"; - } - return `${filesize(value)} (${Math.round((value / max) * 100).toFixed(2)}%)`; - }} - /> - } - /> - - - - - - - - - - ); -} diff --git a/frontend/packages/components/src/actors/actor-metrics-tab.tsx b/frontend/packages/components/src/actors/actor-metrics-tab.tsx deleted file mode 100644 index edc11a6618..0000000000 --- a/frontend/packages/components/src/actors/actor-metrics-tab.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { Button, ScrollArea } from "@rivet-gg/components"; -import { Icon, faBooks } from "@rivet-gg/icons"; -import { ActorMetrics } from "./actor-metrics"; -import type { ActorAtom } from "./actor-context"; - -interface ActorMetricsTabProps { - actor: ActorAtom; -} - -export function ActorMetricsTab(props: ActorMetricsTabProps) { - return ( - -
- -
- -
- ); -} \ No newline at end of file diff --git a/frontend/packages/components/src/actors/actor-metrics.tsx b/frontend/packages/components/src/actors/actor-metrics.tsx deleted file mode 100644 index 947d1166dc..0000000000 --- a/frontend/packages/components/src/actors/actor-metrics.tsx +++ /dev/null @@ -1,704 +0,0 @@ -import { useAtomValue, useSetAtom } from "jotai"; -import { selectAtom } from "jotai/utils"; -import equal from "fast-deep-equal"; -import { useState, useMemo } from "react"; -import type { Actor, ActorAtom } from "./actor-context"; -import { ActorCpuStats } from "./actor-cpu-stats"; -import { ActorMemoryStats } from "./actor-memory-stats"; -import { Dd, Dl, Dt } from "../ui/typography"; -import { Button } from "../ui/button"; -import { Flex } from "../ui/flex"; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select"; -import { actorMetricsTimeWindowAtom, actorEnvironmentAtom } from "./actor-context"; -import { useQuery } from "@tanstack/react-query"; -import { actorMetricsQueryOptions } from "@/domains/project/queries/actors/query-options"; - -const selector = (a: Actor) => ({ - metrics: a.metrics, - status: a.status, - resources: a.resources, - id: a.id, -}); - -const timeWindowOptions = [ - { label: "5 minutes", value: "5m", milliseconds: 5 * 60 * 1000 }, - { label: "15 minutes", value: "15m", milliseconds: 15 * 60 * 1000 }, - { label: "30 minutes", value: "30m", milliseconds: 30 * 60 * 1000 }, - { label: "1 hour", value: "1h", milliseconds: 60 * 60 * 1000 }, - { label: "3 hours", value: "3h", milliseconds: 3 * 60 * 60 * 1000 }, - { label: "6 hours", value: "6h", milliseconds: 6 * 60 * 60 * 1000 }, - { label: "12 hours", value: "12h", milliseconds: 12 * 60 * 60 * 1000 }, - { label: "24 hours", value: "24h", milliseconds: 24 * 60 * 60 * 1000 }, - { label: "2 days", value: "2d", milliseconds: 2 * 24 * 60 * 60 * 1000 }, -]; - -export interface ActorMetricsProps { - actor: ActorAtom; -} - -export function ActorMetrics({ actor }: ActorMetricsProps) { - const { metrics, status, resources, id } = useAtomValue( - selectAtom(actor, selector, equal), - ); - const defaultMetricsData = useAtomValue(metrics); - const [showAdvanced, setShowAdvanced] = useState(false); - - const timeWindowMs = useAtomValue(actorMetricsTimeWindowAtom); - const setTimeWindowMs = useSetAtom(actorMetricsTimeWindowAtom); - const environment = useAtomValue(actorEnvironmentAtom); - - const currentTimeWindow = timeWindowOptions.find(option => option.milliseconds === timeWindowMs) || timeWindowOptions[1]; - const [timeWindow, setTimeWindow] = useState(currentTimeWindow.value); - - const isActorRunning = status === "running"; - - // Create a query for time window-specific metrics - const { data: customMetricsData, status: customMetricsStatus } = useQuery({ - ...actorMetricsQueryOptions( - { - projectNameId: environment?.projectNameId || "", - environmentNameId: environment?.environmentNameId || "", - actorId: id, - timeWindowMs: timeWindowMs, - }, - { refetchInterval: 5000 } - ), - enabled: !!environment && !!id, - }); - - // Use custom metrics if available, otherwise fall back to default - const metricsData = customMetricsData ? { - metrics: customMetricsData.metrics, - rawData: customMetricsData.rawData, - interval: customMetricsData.interval, - status: customMetricsStatus, - updatedAt: Date.now(), - } : defaultMetricsData; - - const handleTimeWindowChange = (value: string) => { - setTimeWindow(value); - const selectedOption = timeWindowOptions.find(option => option.value === value); - if (selectedOption) { - setTimeWindowMs(selectedOption.milliseconds); - } - }; - - const formatBytes = (bytes: number | null | undefined) => { - if (!isActorRunning || bytes === null || bytes === undefined) - return "n/a"; - const mb = bytes / 1024 / 1024; - if (mb < 1024) { - return `${mb.toFixed(1)} MB`; - } - return `${(mb / 1024).toFixed(1)} GB`; - }; - - const formatCpuUsage = (cpu: number | null | undefined) => { - if (!isActorRunning || cpu === null || cpu === undefined) return "n/a"; - return `${(cpu * 100).toFixed(2)}%`; - }; - - const formatNumber = (value: number | null | undefined) => { - if (!isActorRunning || value === null || value === undefined) - return "n/a"; - return value.toLocaleString(); - }; - - const formatTimestamp = (timestamp: number | null | undefined) => { - if (!isActorRunning || timestamp === null || timestamp === undefined) - return "n/a"; - return new Date(timestamp * 1000).toLocaleString(); - }; - - // Calculate CPU percentage using time series data points - const cpuPercentage = useMemo(() => { - if (!isActorRunning) { - return "Stopped"; - } - - const data = metricsData; - if (!data || !data.rawData || !data.interval) { - return "n/a"; - } - - const cpuValues = data.rawData.cpu_usage_seconds_total; - if (!cpuValues || cpuValues.length < 2) { - return "n/a"; - } - - // Find the last valid CPU rate from the most recent data points - let cpuRate = 0; - for (let i = cpuValues.length - 1; i > 0; i--) { - const currentCpu = cpuValues[i]; - const previousCpu = cpuValues[i - 1]; - - if ( - currentCpu !== 0 && - previousCpu !== 0 && - currentCpu >= previousCpu - ) { - const cpuDelta = currentCpu - previousCpu; - const timeDelta = data.interval / 1000; // Convert ms to seconds - - // Rate calculation: CPU seconds used per second of real time - // This gives the fraction of available CPU used (0-1) - cpuRate = (cpuDelta / timeDelta) * 100; - break; - } - } - - return `${Math.min(cpuRate, 100).toFixed(2)}%`; - }, [metricsData, isActorRunning]); - - const calculateMemoryPercentage = ( - usage: number | null | undefined, - ) => { - if ( - !isActorRunning || - usage === null || - usage === undefined || - !resources || - !resources.memory || - resources.memory === 0 - ) { - return null; - } - // Convert usage from bytes to MB and compare with resources.memory (which is in MB) - const usageMB = usage / (1024 * 1024); - return (usageMB / resources.memory) * 100; - }; - - const isLoading = metricsData.status === "pending"; - const hasError = metricsData.status === "error"; - const data = metricsData.metrics || {}; - - if (isLoading) { - return ( -
-

Metrics

-
Loading...
-
- ); - } - - if (hasError) { - return ( -
-

Metrics

-
- Error loading metrics -
-
- ); - } - - const memoryPercentage = calculateMemoryPercentage( - data.memory_usage_bytes, - ); - - return ( -
-
-

Container Metrics

- -
- - {/* Main Metrics */} -
-
-
-
CPU Usage
-
- {cpuPercentage} - {metricsData.rawData?.cpu_usage_seconds_total && - metricsData.rawData.cpu_usage_seconds_total.length > 0 ? ( - - ) : null} -
-
-
-
Memory Usage
-
- - {formatBytes(data.memory_usage_bytes)} - {memoryPercentage !== null && ( - - ({memoryPercentage.toFixed(1)}%) - - )} - - {metricsData.rawData?.memory_usage_bytes && - metricsData.rawData.memory_usage_bytes.length > 0 ? ( - - ) : null} -
-
-
-
- - {/* Advanced Metrics */} - {false && ( - - {/* CPU & Performance */} -
-

CPU & Performance

-
-
CPU Load Average (10s)
-
{formatCpuUsage(data.cpu_load_average_10s)}
-
CPU Usage Seconds Total
-
- {formatNumber(data.cpu_usage_seconds_total)} -
-
CPU User Seconds Total
-
{formatNumber(data.cpu_user_seconds_total)}
-
CPU System Seconds Total
-
- {formatNumber(data.cpu_system_seconds_total)} -
-
CPU Schedstat Run Periods
-
- {formatNumber( - data.cpu_schedstat_run_periods_total, - )} -
-
CPU Schedstat Run Seconds
-
- {formatNumber( - data.cpu_schedstat_run_seconds_total, - )} -
-
CPU Schedstat Runqueue Seconds
-
- {formatNumber( - data.cpu_schedstat_runqueue_seconds_total, - )} -
-
-
- - {/* Memory */} -
-

Memory

-
-
Memory Usage
-
{formatBytes(data.memory_usage_bytes)}
-
Memory Working Set
-
- {formatBytes(data.memory_working_set_bytes)} -
-
Memory RSS
-
{formatBytes(data.memory_rss)}
-
Memory Cache
-
{formatBytes(data.memory_cache)}
-
Memory Swap
-
{formatBytes(data.memory_swap)}
-
Memory Max Usage
-
{formatBytes(data.memory_max_usage_bytes)}
-
Memory Mapped File
-
{formatBytes(data.memory_mapped_file)}
-
Memory Failcnt
-
{formatNumber(data.memory_failcnt)}
-
-
- - {/* Memory Failures */} -
-

Memory Failures

-
-
Page Fault (Container)
-
- {formatNumber( - data.memory_failures_pgfault_container, - )} -
-
Page Fault (Hierarchy)
-
- {formatNumber( - data.memory_failures_pgfault_hierarchy, - )} -
-
Major Page Fault (Container)
-
- {formatNumber( - data.memory_failures_pgmajfault_container, - )} -
-
Major Page Fault (Hierarchy)
-
- {formatNumber( - data.memory_failures_pgmajfault_hierarchy, - )} -
-
-
- - {/* Resource Limits */} -
-

Resource Limits

-
-
Memory Limit
-
{resources?.memory ? `${resources.memory} MB` : "n/a"}
-
CPU Limit
-
{resources?.cpu ? `${resources.cpu / 1000} cores` : "n/a"}
-
-
- - {/* Processes & Threads */} -
-

- Processes & Threads -

-
-
Processes
-
{formatNumber(data.processes)}
-
Threads
-
{formatNumber(data.threads)}
-
Max Threads
-
{formatNumber(data.threads_max)}
-
Tasks Running
-
{formatNumber(data.tasks_state_running)}
-
Tasks Sleeping
-
{formatNumber(data.tasks_state_sleeping)}
-
Tasks Stopped
-
{formatNumber(data.tasks_state_stopped)}
-
Tasks IO Waiting
-
{formatNumber(data.tasks_state_iowaiting)}
-
Tasks Uninterruptible
-
- {formatNumber(data.tasks_state_uninterruptible)} -
-
-
- - {/* Filesystem */} -
-

Filesystem

-
-
Reads Bytes Total (sda)
-
- {formatBytes(data.fs_reads_bytes_total_sda)} -
-
Writes Bytes Total (sda)
-
- {formatBytes(data.fs_writes_bytes_total_sda)} -
-
-
- - {/* Network - Receive */} -
-

Network - Receive

-
-
Bytes Total (eth0)
-
- {formatBytes( - data.network_receive_bytes_total_eth0, - )} -
-
Bytes Total (eth1)
-
- {formatBytes( - data.network_receive_bytes_total_eth1, - )} -
-
Errors Total (eth0)
-
- {formatNumber( - data.network_receive_errors_total_eth0, - )} -
-
Errors Total (eth1)
-
- {formatNumber( - data.network_receive_errors_total_eth1, - )} -
-
Packets Dropped (eth0)
-
- {formatNumber( - data.network_receive_packets_dropped_total_eth0, - )} -
-
Packets Dropped (eth1)
-
- {formatNumber( - data.network_receive_packets_dropped_total_eth1, - )} -
-
Packets Total (eth0)
-
- {formatNumber( - data.network_receive_packets_total_eth0, - )} -
-
Packets Total (eth1)
-
- {formatNumber( - data.network_receive_packets_total_eth1, - )} -
-
-
- - {/* Network - Transmit */} -
-

Network - Transmit

-
-
Bytes Total (eth0)
-
- {formatBytes( - data.network_transmit_bytes_total_eth0, - )} -
-
Bytes Total (eth1)
-
- {formatBytes( - data.network_transmit_bytes_total_eth1, - )} -
-
Errors Total (eth0)
-
- {formatNumber( - data.network_transmit_errors_total_eth0, - )} -
-
Errors Total (eth1)
-
- {formatNumber( - data.network_transmit_errors_total_eth1, - )} -
-
Packets Dropped (eth0)
-
- {formatNumber( - data.network_transmit_packets_dropped_total_eth0, - )} -
-
Packets Dropped (eth1)
-
- {formatNumber( - data.network_transmit_packets_dropped_total_eth1, - )} -
-
Packets Total (eth0)
-
- {formatNumber( - data.network_transmit_packets_total_eth0, - )} -
-
Packets Total (eth1)
-
- {formatNumber( - data.network_transmit_packets_total_eth1, - )} -
-
-
- - {/* TCP Connections */} -
-

TCP Connections

-
-
Close
-
- {formatNumber(data.network_tcp_usage_close)} -
-
Close Wait
-
- {formatNumber( - data.network_tcp_usage_closewait, - )} -
-
Closing
-
- {formatNumber(data.network_tcp_usage_closing)} -
-
Established
-
- {formatNumber( - data.network_tcp_usage_established, - )} -
-
Fin Wait 1
-
- {formatNumber(data.network_tcp_usage_finwait1)} -
-
Fin Wait 2
-
- {formatNumber(data.network_tcp_usage_finwait2)} -
-
Last Ack
-
- {formatNumber(data.network_tcp_usage_lastack)} -
-
Listen
-
- {formatNumber(data.network_tcp_usage_listen)} -
-
Syn Recv
-
- {formatNumber(data.network_tcp_usage_synrecv)} -
-
Syn Sent
-
- {formatNumber(data.network_tcp_usage_synsent)} -
-
Time Wait
-
- {formatNumber(data.network_tcp_usage_timewait)} -
-
-
- - {/* TCP6 Connections */} -
-

TCP6 Connections

-
-
Close
-
- {formatNumber(data.network_tcp6_usage_close)} -
-
Close Wait
-
- {formatNumber( - data.network_tcp6_usage_closewait, - )} -
-
Closing
-
- {formatNumber(data.network_tcp6_usage_closing)} -
-
Established
-
- {formatNumber( - data.network_tcp6_usage_established, - )} -
-
Fin Wait 1
-
- {formatNumber(data.network_tcp6_usage_finwait1)} -
-
Fin Wait 2
-
- {formatNumber(data.network_tcp6_usage_finwait2)} -
-
Last Ack
-
- {formatNumber(data.network_tcp6_usage_lastack)} -
-
Listen
-
- {formatNumber(data.network_tcp6_usage_listen)} -
-
Syn Recv
-
- {formatNumber(data.network_tcp6_usage_synrecv)} -
-
Syn Sent
-
- {formatNumber(data.network_tcp6_usage_synsent)} -
-
Time Wait
-
- {formatNumber(data.network_tcp6_usage_timewait)} -
-
-
- - {/* UDP Connections */} -
-

UDP Connections

-
-
Dropped
-
- {formatNumber(data.network_udp_usage_dropped)} -
-
Listen
-
- {formatNumber(data.network_udp_usage_listen)} -
-
RX Queued
-
- {formatNumber(data.network_udp_usage_rxqueued)} -
-
TX Queued
-
- {formatNumber(data.network_udp_usage_txqueued)} -
-
-
- - {/* UDP6 Connections */} -
-

UDP6 Connections

-
-
Dropped
-
- {formatNumber(data.network_udp6_usage_dropped)} -
-
Listen
-
- {formatNumber(data.network_udp6_usage_listen)} -
-
RX Queued
-
- {formatNumber(data.network_udp6_usage_rxqueued)} -
-
TX Queued
-
- {formatNumber(data.network_udp6_usage_txqueued)} -
-
-
- - {/* System */} -
-

System

-
-
File Descriptors
-
{formatNumber(data.file_descriptors)}
-
Sockets
-
{formatNumber(data.sockets)}
-
Last Seen
-
{formatTimestamp(data.last_seen)}
-
Start Time
-
{formatTimestamp(data.start_time_seconds)}
-
-
-
- )} -
- ); -} \ No newline at end of file diff --git a/frontend/packages/components/src/actors/actor-network.tsx b/frontend/packages/components/src/actors/actor-network.tsx deleted file mode 100644 index 3bb466ad99..0000000000 --- a/frontend/packages/components/src/actors/actor-network.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import { - Button, - Dd, - DiscreteCopyButton, - Dl, - DocsSheet, - Dt, - Flex, - cn, -} from "@rivet-gg/components"; -import { Icon, faBooks } from "@rivet-gg/icons"; -import { useAtomValue } from "jotai"; -import { selectAtom } from "jotai/utils"; -import { Fragment } from "react"; -import type { Actor, ActorAtom } from "./actor-context"; -import { ActorObjectInspector } from "./console/actor-inspector"; - -const selector = (a: Actor) => a.network?.ports; - -export interface ActorNetworkProps { - actor: ActorAtom; -} - -export function ActorNetwork({ actor }: ActorNetworkProps) { - const ports = useAtomValue(selectAtom(actor, selector)); - if (!ports) { - return null; - } - - return ( -
-
-

Network

- - - -
-
- -
-
Ports
-
- {Object.entries(ports).map( - ([name, port], index) => ( - - - {name} - -
-
Protocol
-
- - {port.protocol} - -
-
Port
-
- - {port.port} - -
-
Hostname
-
- - - {port.hostname} - - -
- {port.url ? ( - <> -
URL
-
- - - {port.url} - - -
- - ) : null} - - {port.routing?.host ? ( - <> -
Host Routing
-
- - - -
- - ) : null} -
-
- ), - )} -
-
-
-
-
- ); -} diff --git a/frontend/packages/components/src/actors/actor-not-found.tsx b/frontend/packages/components/src/actors/actor-not-found.tsx deleted file mode 100644 index c1beda2464..0000000000 --- a/frontend/packages/components/src/actors/actor-not-found.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { Icon, faQuestionSquare } from "@rivet-gg/icons"; -import { useAtomValue, useSetAtom } from "jotai"; -import { selectAtom } from "jotai/utils"; -import { useCallback } from "react"; -import { Button } from "../ui/button"; -import { FilterOp } from "../ui/filters"; -import { - type ActorFeature, - actorFiltersAtom, - currentActorQueryAtom, -} from "./actor-context"; -import { ActorTabs } from "./actors-actor-details"; -import { useActorsView } from "./actors-view-context-provider"; -import { ShimmerLine } from "../shimmer-line"; - -export function ActorNotFound({ - features = [], -}: { features?: ActorFeature[] }) { - const { copy } = useActorsView(); - - const setFilters = useSetAtom(actorFiltersAtom); - const hasDevMode = useAtomValue( - selectAtom( - actorFiltersAtom, - useCallback((filters) => filters.devMode, []), - ), - ); - - const { isLoading } = useAtomValue(currentActorQueryAtom); - - return ( -
- -
- {!isLoading ? ( - <> - -

- {copy.actorNotFound} -

-

- {copy.actorNotFoundDescription} -

- - ) : null} - - {!hasDevMode && !isLoading ? ( - - ) : null} - {isLoading ? : null} -
-
-
- ); -} diff --git a/frontend/packages/components/src/actors/actor-region.tsx b/frontend/packages/components/src/actors/actor-region.tsx deleted file mode 100644 index 12b7eb3e2e..0000000000 --- a/frontend/packages/components/src/actors/actor-region.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { Flex, WithTooltip } from "@rivet-gg/components"; -import { useAtomValue } from "jotai"; -import { selectAtom } from "jotai/utils"; -import { useCallback } from "react"; -import { - REGION_LABEL, - RegionIcon, - getRegionKey, -} from "../matchmaker/lobby-region"; -import { actorRegionsAtom } from "./actor-context"; - -interface ActorRegionProps { - regionId?: string; - showLabel?: boolean | "abbreviated"; - className?: string; -} - -export function ActorRegion({ - showLabel, - regionId, - className, -}: ActorRegionProps) { - const region = useAtomValue( - selectAtom( - actorRegionsAtom, - useCallback( - (regions) => regions.find((region) => region.id === regionId), - [regionId], - ), - ), - ); - - const regionKey = getRegionKey(region?.id); - - if (showLabel) { - return ( - - - - {showLabel === "abbreviated" - ? regionKey.toUpperCase() - : (REGION_LABEL[regionKey] ?? REGION_LABEL.unknown)} - - - ); - } - - return ( - - - - } - /> - ); -} diff --git a/frontend/packages/components/src/actors/actor-runtime.tsx b/frontend/packages/components/src/actors/actor-runtime.tsx deleted file mode 100644 index e8c0f181a8..0000000000 --- a/frontend/packages/components/src/actors/actor-runtime.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import equal from "fast-deep-equal"; -import { useAtomValue } from "jotai"; -import { selectAtom } from "jotai/utils"; -import { Suspense } from "react"; -import { formatDuration } from "../lib/formatter"; -import { toRecord } from "../lib/utils"; -import { Flex } from "../ui/flex"; -import { Skeleton } from "../ui/skeleton"; -import { Dd, Dl, Dt } from "../ui/typography"; -import { ActorBuild } from "./actor-build"; -import { - type Actor, - type ActorAtom, - ActorFeature, - currentActorFeaturesAtom, -} from "./actor-context"; -import { ACTOR_FRAMEWORK_TAG_VALUE } from "./actor-tags"; -import { ActorObjectInspector } from "./console/actor-inspector"; - -const selector = (a: Actor) => ({ - lifecycle: a.lifecycle, - resources: a.resources, - runtime: a.runtime, - tags: a.tags, -}); - -export interface ActorRuntimeProps { - actor: ActorAtom; -} - -export function ActorRuntime({ actor }: ActorRuntimeProps) { - const { lifecycle, resources, runtime, tags } = useAtomValue( - selectAtom(actor, selector, equal), - ); - - const features = useAtomValue(currentActorFeaturesAtom); - - return ( - <> - {features.includes(ActorFeature.Runtime) && lifecycle && runtime ? ( -
-
-

Runtime

-
- -
-
Kill timeout
-
- {formatDuration(lifecycle.killTimeout || 0, { - show0Min: true, - })} -
- {toRecord(tags).framework !== - ACTOR_FRAMEWORK_TAG_VALUE && resources ? ( - <> -
Resources
-
- {resources.cpu / 1000} CPU cores,{" "} - {resources.memory} MB RAM -
- - ) : null} -
Arguments
-
- -
-
Environment
-
- -
- -
Durable
-
{lifecycle.durable ? "Yes" : "No"}
-
-
-
- ) : null} - - } - > - - - - ); -} diff --git a/frontend/packages/components/src/actors/actor-state-change-indicator.tsx b/frontend/packages/components/src/actors/actor-state-change-indicator.tsx deleted file mode 100644 index b5d1400d85..0000000000 --- a/frontend/packages/components/src/actors/actor-state-change-indicator.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { Badge } from "@rivet-gg/components"; -import { motion } from "framer-motion"; -import { useEffect, useRef } from "react"; - -const EMPTY_OBJECT = {}; - -interface ActorStateChangeIndicatorProps { - state: unknown | undefined; -} - -export function ActorStateChangeIndicator({ - state, -}: ActorStateChangeIndicatorProps) { - const isMounted = useRef(false); - const oldState = useRef(); - - useEffect(() => { - isMounted.current = true; - }, []); - - useEffect(() => { - oldState.current = state || EMPTY_OBJECT; - }, [state]); - - const hasChanged = state !== oldState.current; - const shouldUpdate = hasChanged && isMounted.current; - - return ( - - - State changed - - - ); -} diff --git a/frontend/packages/components/src/actors/actor-state-tab.tsx b/frontend/packages/components/src/actors/actor-state-tab.tsx deleted file mode 100644 index 634596a9b5..0000000000 --- a/frontend/packages/components/src/actors/actor-state-tab.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { useAtomValue } from "jotai"; -import { selectAtom } from "jotai/utils"; -import type { Actor, ActorAtom } from "./actor-context"; -import { ActorEditableState } from "./actor-editable-state"; -import { - useActorState, - useActorWorkerStatus, -} from "./worker/actor-worker-context"; - -const selector = (a: Actor) => a.destroyedAt; - -interface ActorStateTabProps { - actor: ActorAtom; -} - -export function ActorStateTab({ actor }: ActorStateTabProps) { - const destroyedAt = useAtomValue(selectAtom(actor, selector)); - const status = useActorWorkerStatus(); - - const state = useActorState(); - - if (destroyedAt) { - return ( -
- State Preview is unavailable for inactive Actors. -
- ); - } - - if (status.type === "error") { - return ( -
- State Preview is currently unavailable. -
- See console/logs for more details. -
- ); - } - - if (status.type === "unsupported") { - return ( -
- State Preview is not supported for this Actor. -
- ); - } - - if (status.type !== "ready") { - return ( -
- Loading state... -
- ); - } - - return ( -
- -
- ); -} diff --git a/frontend/packages/components/src/actors/actor-status-indicator.tsx b/frontend/packages/components/src/actors/actor-status-indicator.tsx deleted file mode 100644 index 81f9ff34dc..0000000000 --- a/frontend/packages/components/src/actors/actor-status-indicator.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { Ping, cn } from "@rivet-gg/components"; -import { useAtomValue } from "jotai"; -import { selectAtom } from "jotai/utils"; -import type { ComponentPropsWithRef } from "react"; -import type { Actor, ActorAtom } from "./actor-context"; - -export type ActorStatus = - | "starting" - | "running" - | "stopped" - | "crashed" - | "unknown"; - -export function getActorStatus( - actor: Pick, -): ActorStatus { - const { createdAt, startedAt, destroyedAt } = actor; - - if (createdAt && !startedAt && !destroyedAt) { - return "starting"; - } - - if (createdAt && startedAt && !destroyedAt) { - return "running"; - } - - if (createdAt && startedAt && destroyedAt) { - return "stopped"; - } - - if (createdAt && !startedAt && destroyedAt) { - return "crashed"; - } - - return "unknown"; -} - -interface AtomizedActorStatusIndicatorProps - extends ComponentPropsWithRef<"span"> { - actor: ActorAtom; -} - -export const AtomizedActorStatusIndicator = ({ - actor, - ...props -}: AtomizedActorStatusIndicatorProps) => { - const status = useAtomValue(selectAtom(actor, selector)); - return ; -}; - -const selector = ({ status }: Actor) => status; - -interface ActorStatusIndicatorProps extends ComponentPropsWithRef<"span"> { - status: ReturnType; -} - -export const ActorStatusIndicator = ({ - status, - ...props -}: ActorStatusIndicatorProps) => { - if (status === "running") { - return ( - - ); - } - - return ( - - ); -}; diff --git a/frontend/packages/components/src/actors/actor-status-label.tsx b/frontend/packages/components/src/actors/actor-status-label.tsx deleted file mode 100644 index c6a16ddb7d..0000000000 --- a/frontend/packages/components/src/actors/actor-status-label.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { useAtomValue } from "jotai"; -import { selectAtom } from "jotai/utils"; -import type { Actor, ActorAtom } from "./actor-context"; -import type { ActorStatus } from "./actor-status-indicator"; - -export const ACTOR_STATUS_LABEL_MAP = { - unknown: "Unknown", - starting: "Starting", - running: "Running", - stopped: "Stopped", - crashed: "Crashed", -} satisfies Record; - -export const ActorStatusLabel = ({ status }: { status: ActorStatus }) => { - return {ACTOR_STATUS_LABEL_MAP[status]}; -}; - -const selector = (a: Actor) => a.status; - -export const AtomizedActorStatusLabel = ({ - actor, -}: { - actor: ActorAtom; -}) => { - const status = useAtomValue(selectAtom(actor, selector)); - return ; -}; diff --git a/frontend/packages/components/src/actors/actor-status.tsx b/frontend/packages/components/src/actors/actor-status.tsx deleted file mode 100644 index 0f587ed7cb..0000000000 --- a/frontend/packages/components/src/actors/actor-status.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { cn } from "@rivet-gg/components"; -import type { ActorAtom } from "./actor-context"; -import { - ActorStatusIndicator, - type ActorStatus as ActorStatusType, - AtomizedActorStatusIndicator, -} from "./actor-status-indicator"; -import { - ActorStatusLabel, - AtomizedActorStatusLabel, -} from "./actor-status-label"; - -interface ActorStatusProps { - className?: string; - actor: ActorAtom; -} - -export const AtomizedActorStatus = ({ - className, - ...props -}: ActorStatusProps) => { - return ( -
- - -
- ); -}; - -export const ActorStatus = ({ - className, - status, -}: { - className?: string; - status: ActorStatusType; -}) => { - return ( -
- - -
- ); -}; diff --git a/frontend/packages/components/src/actors/actor-stop-button.tsx b/frontend/packages/components/src/actors/actor-stop-button.tsx deleted file mode 100644 index cbc67ea89f..0000000000 --- a/frontend/packages/components/src/actors/actor-stop-button.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { Button, WithTooltip } from "@rivet-gg/components"; -import { Icon, faXmark } from "@rivet-gg/icons"; - -import equal from "fast-deep-equal"; -import { useAtomValue } from "jotai"; -import { selectAtom } from "jotai/utils"; -import type { Actor, ActorAtom, DestroyActorAtom } from "./actor-context"; - -const selector = (a: Actor) => ({ - destroyedAt: a.destroyedAt, - destroy: a.destroy, -}); - -interface ActorStopButtonProps { - actor: ActorAtom; -} - -export function ActorStopButton({ actor }: ActorStopButtonProps) { - const { destroy: destroyAtom, destroyedAt } = useAtomValue( - selectAtom(actor, selector, equal), - ); - - if (destroyedAt || !destroyAtom) { - return null; - } - - return ; -} - -function Content({ destroy: destroyAtom }: { destroy: DestroyActorAtom }) { - const { destroy, isDestroying } = useAtomValue(destroyAtom); - return ( - - - - } - content="Stop Actor" - /> - ); -} diff --git a/frontend/packages/components/src/actors/actor-tags-select.tsx b/frontend/packages/components/src/actors/actor-tags-select.tsx deleted file mode 100644 index 17eedbba3e..0000000000 --- a/frontend/packages/components/src/actors/actor-tags-select.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { Combobox } from "@rivet-gg/components"; -import { useAtomValue } from "jotai"; -import { useMemo } from "react"; -import { actorTagsAtom } from "./actor-context"; -import { ActorTag } from "./actor-tags"; - -interface ActorTagsSelectProps { - value: Record; - onValueChange: (value: Record) => void; - showSelectedOptions?: number; -} - -export function ActorTagsSelect({ - value, - onValueChange, - showSelectedOptions, -}: ActorTagsSelectProps) { - const data = useAtomValue(actorTagsAtom); - - const valArray = useMemo(() => Object.entries(value), [value]); - const tags = useMemo(() => { - // upsert custom tags to the list of tags - const tags = [...data]; - for (const [key, value] of valArray) { - const found = data.find( - (tag) => tag.key === key && tag.value === value, - ); - - if (!found) { - tags.push({ key, value }); - } - } - return tags; - }, [valArray, data]); - - const val = useMemo( - () => - valArray.map(([key, value]) => { - return [key, value].join("="); - }), - [valArray], - ); - - const options = useMemo( - () => - tags.map((tag) => { - return { - label: ( - - - {tag.key}={tag.value} - - - ), - value: [tag.key, tag.value].join("="), - tag, - }; - }), - [tags], - ); - - const handleValueChange = (value: string[]) => { - onValueChange( - Object.fromEntries( - value.map((v) => { - // its safe to split by "=" because the value is a tag - const [key, value] = v.split("="); - return [key.trim(), value.trim()]; - }), - ), - ); - }; - - const handleCreateOption = (option: string) => { - const parts = option.split("="); - if (parts.length !== 2) return; - - const [key, value] = parts.map((part) => part.trim()); - if (!key || !value) return; - - onValueChange(Object.fromEntries([...valArray, [key, value]])); - }; - - return ( - { - const tagKey = option.tag.key.toLowerCase(); - const tagValue = option.tag.value.toLowerCase(); - - if (search.includes("=")) { - const [key, value] = search.split("="); - return tagKey.includes(key) && tagValue.includes(value); - } - return tagKey.includes(search) || tagValue.includes(search); - }} - className="w-full min-w-[20rem]" - /> - ); -} diff --git a/frontend/packages/components/src/actors/actor-tags.tsx b/frontend/packages/components/src/actors/actor-tags.tsx deleted file mode 100644 index 5a017d5cb0..0000000000 --- a/frontend/packages/components/src/actors/actor-tags.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import { - Button, - DiscreteCopyButton, - Slot, - Slottable, - WithTooltip, - cn, -} from "@rivet-gg/components"; -import { Icon, faTag } from "@rivet-gg/icons"; -import { type ReactNode, forwardRef, useState } from "react"; - -const BUILT_IN_TAGS = { - actors: ["framework", "framework-version"], - builds: ["current"], -}; - -export const ACTOR_FRAMEWORK_TAG_VALUE = "rivetkit"; - -export const ActorTag = forwardRef< - HTMLSpanElement, - { children: ReactNode; className?: string } ->(({ children, className, ...props }, ref) => ( - - - {children} - -)); - -interface ActorTagsProps { - tags?: unknown; - excludeBuiltIn?: keyof typeof BUILT_IN_TAGS; - className?: string; - truncate?: boolean; - copy?: boolean; - max?: number; - hoverable?: boolean; -} - -export function ActorTags({ - tags = {}, - excludeBuiltIn = undefined, - truncate = true, - className, - hoverable = true, - max = Number.POSITIVE_INFINITY, - copy = true, -}: ActorTagsProps) { - const withoutBuiltIn = Object.entries(tags ?? {}).filter(([key]) => - excludeBuiltIn ? !BUILT_IN_TAGS[excludeBuiltIn].includes(key) : true, - ); - - const [isTruncatedList, setTruncatedList] = useState( - withoutBuiltIn.length > max, - ); - - const truncated = withoutBuiltIn.filter((_, index) => - isTruncatedList ? index < max : true, - ); - - const truncatedCount = withoutBuiltIn.length - truncated.length; - - return ( -
- {truncated.length > 0 ? ( - <> - {truncated - .sort(([a], [b]) => a.localeCompare(b)) - .map(([key, value]) => { - let trigger = truncate ? ( - - - {key}={value} - - - ) : ( - - - {key}={value} - - - ); - - trigger = copy ? ( - - - {trigger} - - - ) : ( - trigger - ); - - return truncate && hoverable && !copy ? ( - - ) : ( - trigger - ); - })} - - {truncatedCount > 0 ? ( - - ) : null} - - ) : null} -
- ); -} diff --git a/frontend/packages/components/src/actors/actors-actor-details.tsx b/frontend/packages/components/src/actors/actors-actor-details.tsx deleted file mode 100644 index e45a7bc48e..0000000000 --- a/frontend/packages/components/src/actors/actors-actor-details.tsx +++ /dev/null @@ -1,240 +0,0 @@ -import { - Flex, - Tabs, - TabsContent, - TabsList, - TabsTrigger, - cn, -} from "@rivet-gg/components"; -import { Icon, faQuestionSquare } from "@rivet-gg/icons"; -import { useAtomValue } from "jotai"; -import { type ReactNode, Suspense, memo } from "react"; -import { ActorConfigTab } from "./actor-config-tab"; -import { ActorConnectionsTab } from "./actor-connections-tab"; -import { - type ActorAtom, - ActorFeature, - currentActorFeaturesAtom, -} from "./actor-context"; -import { ActorDetailsSettingsProvider } from "./actor-details-settings"; -import { ActorLogsTab } from "./actor-logs-tab"; -import { ActorMetricsTab } from "./actor-metrics-tab"; -import { ActorStateTab } from "./actor-state-tab"; -import { AtomizedActorStatus } from "./actor-status"; -import { ActorStopButton } from "./actor-stop-button"; -import { ActorsSidebarToggleButton } from "./actors-sidebar-toggle-button"; -import { useActorsView } from "./actors-view-context-provider"; -import { ActorConsole } from "./console/actor-console"; -import { ActorWorkerContextProvider } from "./worker/actor-worker-context"; - -interface ActorsActorDetailsProps { - tab?: string; - actor: ActorAtom; - onTabChange?: (tab: string) => void; - onExportLogs?: ( - actorId: string, - typeFilter?: string, - filter?: string, - ) => Promise; - isExportingLogs?: boolean; -} - -export const ActorsActorDetails = memo( - ({ - tab, - onTabChange, - actor, - onExportLogs, - isExportingLogs, - }: ActorsActorDetailsProps) => { - const actorFeatures = useAtomValue(currentActorFeaturesAtom); - const supportsConsole = actorFeatures?.includes(ActorFeature.Console); - - return ( - - -
- - - {supportsConsole ? : null} -
-
-
- ); - }, -); - -export const ActorsActorEmptyDetails = ({ - features, -}: { - features: ActorFeature[]; -}) => { - const { copy } = useActorsView(); - return ( -
- -
- -

{copy.selectActor}

-
-
-
- ); -}; - -export function ActorTabs({ - tab, - features, - onTabChange, - actor, - className, - disabled, - children, - onExportLogs, - isExportingLogs, -}: { - disabled?: boolean; - tab?: string; - features: ActorFeature[]; - onTabChange?: (tab: string) => void; - actor?: ActorAtom; - className?: string; - children?: ReactNode; - onExportLogs?: ( - actorId: string, - typeFilter?: string, - filter?: string, - ) => Promise; - isExportingLogs?: boolean; -}) { - const supportsState = features?.includes(ActorFeature.State); - const supportsLogs = features?.includes(ActorFeature.Logs); - const supportsConnections = features?.includes(ActorFeature.Connections); - const supportsConfig = features?.includes(ActorFeature.Config); - const supportsMetrics = features?.includes(ActorFeature.Metrics); - - const defaultTab = supportsState ? "state" : "logs"; - const value = disabled ? undefined : tab || defaultTab; - - return ( - -
- -
- - {supportsState ? ( - - State - - ) : null} - {supportsConnections ? ( - - Connections - - ) : null} - {supportsLogs ? ( - - Logs - - ) : null} - {supportsConfig ? ( - - Config - - ) : null} - {supportsMetrics ? ( - - Metrics - - ) : null} - - {actor ? ( - - - - - ) : null} -
-
- {actor ? ( - <> - {supportsLogs ? ( - - }> - - - - ) : null} - {supportsConfig ? ( - - - - ) : null} - {supportsConnections ? ( - - - - ) : null} - {supportsState ? ( - - - - ) : null} - {supportsMetrics ? ( - - - - ) : null} - - ) : null} - {children} -
- ); -} diff --git a/frontend/packages/components/src/actors/actors-actor-not-found.tsx b/frontend/packages/components/src/actors/actors-actor-not-found.tsx deleted file mode 100644 index 96fed3be76..0000000000 --- a/frontend/packages/components/src/actors/actors-actor-not-found.tsx +++ /dev/null @@ -1,33 +0,0 @@ -// import { isRivetError } from "@/lib/utils"; -// import { RivetError } from "@rivet-gg/api"; -import { Icon, faCircleExclamation } from "@rivet-gg/icons"; -import type { ErrorComponentProps } from "@tanstack/react-router"; -import { ActorsSidebarToggleButton } from "./actors-sidebar-toggle-button"; - -export function ActorsActorError({ error }: ErrorComponentProps) { - // if (isRivetError(error) || error instanceof RivetError) { - // return ( - //
- //
- // - //
- //
- // - // Actor not found. - //
- //
- // ); - // } - - return ( -
-
- -
-
- - Error occurred while fetching Actor. -
-
- ); -} diff --git a/frontend/packages/components/src/actors/actors-layout-context.tsx b/frontend/packages/components/src/actors/actors-layout-context.tsx deleted file mode 100644 index a40a84c760..0000000000 --- a/frontend/packages/components/src/actors/actors-layout-context.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import type { ReactNode } from "react"; -import { createContext, useContext, useMemo } from "react"; -import { assertNonNullable } from "../lib/utils"; - -export interface ActorsLayoutContextValue { - isFolded: boolean; - setFolded: (value: boolean) => void; -} - -export const ActorsLayoutContext = - createContext(null); - -interface ActorsLayoutProviderProps extends ActorsLayoutContextValue { - children: ReactNode; -} - -export function ActorsLayoutContextProvider({ - children, - isFolded, - setFolded, -}: ActorsLayoutProviderProps) { - return ( - ({ isFolded, setFolded }), - [isFolded, setFolded], - )} - > - {children} - - ); -} - -export function useActorsLayout() { - const context = useContext(ActorsLayoutContext); - assertNonNullable(context); - return context; -} diff --git a/frontend/packages/components/src/actors/actors-layout.tsx b/frontend/packages/components/src/actors/actors-layout.tsx deleted file mode 100644 index df2a8d4b32..0000000000 --- a/frontend/packages/components/src/actors/actors-layout.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { type ReactNode, memo, useState } from "react"; -import { cn, ls } from "../lib/utils"; -import { ActorsLayoutContextProvider } from "./actors-layout-context"; - -interface ActorsListPreviewProps { - left: ReactNode; - right: ReactNode; - className?: string; -} - -export const ActorsLayout = memo( - ({ left, right, className }: ActorsListPreviewProps) => { - const [folded, setFolded] = useState(() => ls.actorsList.getFolded()); - - return ( - -
- {left} -
- {right} -
-
-
- ); - }, -); diff --git a/frontend/packages/components/src/actors/actors-list-panel.tsx b/frontend/packages/components/src/actors/actors-list-panel.tsx deleted file mode 100644 index aea9d29000..0000000000 --- a/frontend/packages/components/src/actors/actors-list-panel.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { ActorsList } from "./actors-list"; - -export function ActorsListPanel() { - return ; -} diff --git a/frontend/packages/components/src/actors/actors-list-preview.tsx b/frontend/packages/components/src/actors/actors-list-preview.tsx deleted file mode 100644 index d26a22acab..0000000000 --- a/frontend/packages/components/src/actors/actors-list-preview.tsx +++ /dev/null @@ -1,158 +0,0 @@ -import { Icon, faGripDotsVertical } from "@rivet-gg/icons"; -import { - animate, - motion, - useMotionTemplate, - useMotionValue, - useMotionValueEvent, - useTransform, -} from "framer-motion"; -import { - type ReactNode, - Suspense, - memo, - useCallback, - useLayoutEffect, - useState, -} from "react"; -import { cn, ls } from "../lib/utils"; -import { ActorsLayoutContextProvider } from "./actors-layout-context"; -import { ActorsListPanel } from "./actors-list-panel"; - -const RIGHT_PANEL_MIN_WIDTH = 480; - -interface ActorsListPreviewProps { - children: ReactNode; -} - -export const ActorsListPreview = memo( - ({ children }: ActorsListPreviewProps) => { - const outerWidth = useMotionValue(0); - const x = useMotionValue(0); - - const rightWidth = useMotionTemplate`calc(50% - ${x}px)`; - const leftWidth = useMotionTemplate`calc(50% + ${x}px)`; - - const [folded, setFolded] = useState(() => ls.actorsList.getFolded()); - const [isDragging, setIsDragging] = useState(false); - - const [, setInitialized] = useState(false); - - useMotionValueEvent(x, "change", (value) => { - ls.actorsList.set(value / outerWidth.get(), folded); - }); - - // biome-ignore lint/correctness/useExhaustiveDependencies: on first draw - useLayoutEffect(() => { - x.setCurrent((ls.actorsList.getWidth() || 0) * outerWidth.get()); - setInitialized(true); - }, []); - - const relativeOffset = useTransform( - [x, outerWidth], - ([value, outerWidth]: number[]) => { - const center = outerWidth / 2; - const percent = (value + center) / outerWidth; - // 0.5 is the center - // 0 is the left - // 1 is the right - return percent; - }, - ); - - const opacity = useTransform(relativeOffset, [0, 0.1], [0, 1]); - const pointerEvents = useTransform(opacity, () => { - return opacity.get() > 0.5 ? "auto" : "none"; - }); - - const toggle = useCallback( - (newValue: boolean) => { - setFolded(newValue); - if (newValue) { - animate(x, -outerWidth.get() / 2); - } else { - animate(x, 0); - } - }, - [outerWidth, x], - ); - - return ( - -
{ - if (ref) { - const width = ref.getBoundingClientRect().width; - outerWidth.set(width); - } - }} - > - - - - setIsDragging(true)} - onDrag={(e, info) => { - const rightPos = outerWidth.get() - info.point.x; - setFolded(outerWidth.get() - rightPos < 470); - }} - onDoubleClick={() => { - setFolded(!folded); - if (folded) { - animate(x, 0); - } else { - animate(x, -outerWidth.get() / 2); - } - }} - onDragEnd={(e, info) => { - if (folded) { - animate(x, -outerWidth.get() / 2); - } else { - const leftPos = outerWidth.get() - info.point.x; - if (leftPos < RIGHT_PANEL_MIN_WIDTH) { - animate( - x, - outerWidth.get() / 2 - - RIGHT_PANEL_MIN_WIDTH, - ); - } - } - setIsDragging(false); - }} - dragMomentum={false} - className="w-[1px] bg-border cursor-col-resize inset-y-0 z-20 relative flex items-center group" - > - - - - {children} - -
-
- ); - }, -); diff --git a/frontend/packages/components/src/actors/actors-list-row.tsx b/frontend/packages/components/src/actors/actors-list-row.tsx deleted file mode 100644 index 6108b4df0c..0000000000 --- a/frontend/packages/components/src/actors/actors-list-row.tsx +++ /dev/null @@ -1,168 +0,0 @@ -import { - Button, - RelativeTime, - SmallText, - WithTooltip, - cn, - toRecord, -} from "@rivet-gg/components"; -import { Icon, faTag, faTags } from "@rivet-gg/icons"; -import { Link } from "@tanstack/react-router"; -import { useAtomValue } from "jotai"; -import { selectAtom } from "jotai/utils"; -import { memo } from "react"; -import { - type Actor, - type ActorAtom, - isCurrentActorAtom, -} from "./actor-context"; -import { ActorRegion } from "./actor-region"; -import { AtomizedActorStatusIndicator } from "./actor-status-indicator"; -import { AtomizedActorStatusLabel } from "./actor-status-label"; -import { ActorTags } from "./actor-tags"; - -interface ActorsListRowProps { - className?: string; - actor: ActorAtom; -} - -const selector = (actor: Actor) => actor.id; - -export const ActorsListRow = memo( - ({ className, actor }: ActorsListRowProps) => { - const id = useAtomValue(selectAtom(actor, selector)); - const isCurrent = useAtomValue(isCurrentActorAtom(actor)); - - return ( - - ); - }, -); - -const regionSelector = (actor: Actor) => actor.region; - -function Region({ actor }: { actor: ActorAtom }) { - const regionId = useAtomValue(selectAtom(actor, regionSelector)); - - return ( - - ); -} - -const tagsSelector = (actor: Actor) => toRecord(actor.tags); - -function Tags({ actor }: { actor: ActorAtom }) { - const tags = useAtomValue(selectAtom(actor, tagsSelector)); - - const tagCount = Object.keys(tags).length; - - return ( - - -
- - {Object.keys(tags).length}{" "} - {tagCount === 1 ? "tag" : "tags"} -
- - } - content={ - <> -

Tags

- - - } - /> - ); -} - -const createdAtSelector = (actor: Actor) => actor.createdAt; - -function CreatedAt({ actor }: { actor: ActorAtom }) { - const createdAt = useAtomValue(selectAtom(actor, createdAtSelector)); - - return ( - - {createdAt ? ( - } - content={createdAt.toLocaleString()} - /> - ) : ( - - - )} - - ); -} - -const destroyedAtSelector = (actor: Actor) => actor.destroyedAt; -function DestroyedAt({ actor }: { actor: ActorAtom }) { - const destroyedAt = useAtomValue(selectAtom(actor, destroyedAtSelector)); - - return ( - - {destroyedAt ? ( - } - content={new Date(destroyedAt).toLocaleString()} - /> - ) : ( - - - )} - - ); -} diff --git a/frontend/packages/components/src/actors/actors-list.tsx b/frontend/packages/components/src/actors/actors-list.tsx deleted file mode 100644 index 2ca0e8242d..0000000000 --- a/frontend/packages/components/src/actors/actors-list.tsx +++ /dev/null @@ -1,489 +0,0 @@ -import { - Button, - Checkbox, - CommandGroup, - CommandItem, - DocsSheet, - FilterCreator, - type FilterDefinitions, - FilterOp, - type OnFiltersChange, - type OptionsProviderProps, - ScrollArea, - ShimmerLine, - SmallText, - cn, - createFiltersPicker, - createFiltersSchema, -} from "@rivet-gg/components"; -import { - Icon, - faActors, - faCalendarCircleMinus, - faCalendarCirclePlus, - faCalendarMinus, - faCalendarPlus, - faCode, - faGlobe, - faReact, - faSignalBars, - faTag, - faTs, -} from "@rivet-gg/icons"; -import { useNavigate, useSearch } from "@tanstack/react-router"; -import { useAtomValue, useSetAtom } from "jotai"; -import { useCallback, useMemo } from "react"; -import { - actorFiltersAtom, - actorFiltersCountAtom, - actorRegionsAtom, - actorTagsAtom, - actorsAtomsAtom, - actorsPaginationAtom, - actorsQueryAtom, - filteredActorsCountAtom, -} from "./actor-context"; -import { ActorStatus } from "./actor-status"; -import type { ActorStatus as ActorStatusType } from "./actor-status-indicator"; -import { ActorTag } from "./actor-tags"; -import { ActorsListRow } from "./actors-list-row"; -import { useActorsView } from "./actors-view-context-provider"; -import { CreateActorButton } from "./create-actor-button"; -import { GoToActorButton } from "./go-to-actor-button"; - -export function ActorsList() { - return ( - <> - -
-
-
- -
- - -
- -
-
-
-
- - Region - - - - -
-
ID
-
Tags
-
- - Created - - - - -
-
- - Destroyed - - - - -
-
-
- - -
- - - ); -} - -function LoadingIndicator() { - const state = useAtomValue(actorsQueryAtom); - if (state.isLoading) { - return ; - } - return null; -} - -function List() { - const actors = useAtomValue(actorsAtomsAtom); - return ( - <> - {actors.map((actor) => ( - - ))} - - ); -} - -function Pagination() { - const { hasNextPage, isFetchingNextPage, fetchNextPage } = - useAtomValue(actorsPaginationAtom); - - if (hasNextPage) { - return ( -
- -
- ); - } - - return ; -} - -function EmptyState() { - const count = useAtomValue(filteredActorsCountAtom); - const filtersCount = useAtomValue(actorFiltersCountAtom); - const setFilters = useSetAtom(actorFiltersAtom); - const { copy } = useActorsView(); - - return ( -
- {count === 0 ? ( - filtersCount === 0 ? ( -
- - - {copy.noActorsFound} - -
- {" "} - - Use one of the quick start guides to get - started. - -
- - - - - - -
-
-
- ) : ( - <> - - {copy.noActorsMatchFilter} - - - - ) - ) : ( - - {copy.noMoreActors} - - )} -
- ); -} - -const FILTER_DEFINITIONS = { - tags: { - type: "select", - label: "Tags", - icon: faTag, - options: TagsOptions, - operators: { - [FilterOp.EQUAL]: "is one of", - [FilterOp.NOT_EQUAL]: "is not one of", - }, - }, - createdAt: { - type: "date", - label: "Created", - icon: faCalendarCirclePlus, - }, - destroyedAt: { - type: "date", - label: "Destroyed", - icon: faCalendarCircleMinus, - }, - status: { - type: "select", - label: "Status", - icon: faSignalBars, - options: StatusOptions, - display: ({ value }) => { - if (value.length > 1) { - return {value.length} statuses; - } - return ( - - ); - }, - }, - region: { - type: "select", - label: "Region", - icon: faGlobe, - options: RegionOptions, - display: ({ value }) => { - if (value.length > 1) { - return {value.length} regions; - } - const region = useAtomValue(actorRegionsAtom).find( - (region) => region.id === value[0], - ); - return {region?.name}; - }, - operators: { - [FilterOp.EQUAL]: "is one of", - [FilterOp.NOT_EQUAL]: "is not one of", - }, - }, - devMode: { - type: "boolean", - label: "Show hidden actors", - icon: faCode, - }, -} satisfies FilterDefinitions; - -export const ActorsListFiltersSchema = createFiltersSchema(FILTER_DEFINITIONS); - -export const pickActorListFilters = createFiltersPicker(FILTER_DEFINITIONS); - -function Filters() { - const navigate = useNavigate(); - const filters = useSearch({ strict: false }); - - const onFiltersChange: OnFiltersChange = useCallback( - (fnOrValue) => { - if (typeof fnOrValue === "function") { - navigate({ - search: ({ actorId, tab, ...filters }) => ({ - actorId, - tab, - ...Object.fromEntries( - Object.entries(fnOrValue(filters)).filter( - ([, filter]) => filter.value.length > 0, - ), - ), - }), - }); - } else { - navigate({ - search: (value) => ({ - actorId: value.actorId, - tab: value.tab, - ...Object.fromEntries( - Object.entries(fnOrValue).filter( - ([, filter]) => filter.value.length > 0, - ), - ), - }), - }); - } - }, - [navigate], - ); - - const { copy } = useActorsView(); - - const filtersDefs = useMemo(() => { - return { - ...FILTER_DEFINITIONS, - devMode: { - ...FILTER_DEFINITIONS.devMode, - hidden: true, - label: copy.showHiddenActors, - }, - }; - }, [copy.showHiddenActors]); - - return ( - - ); -} - -function TagsOptions({ onSelect, value: filterValue }: OptionsProviderProps) { - const tags = useAtomValue(actorTagsAtom); - - const values = filterValue.map((filter) => filter.split("=")); - - return ( - - {tags.map(({ key, value }) => { - const isSelected = values.some( - ([filterKey, filterValue]) => - filterKey === key && filterValue === value, - ); - return ( - { - if (isSelected) { - onSelect( - values - .filter( - ([filterKey, filterValue]) => - filterKey !== key || - filterValue !== value, - ) - .map((pair) => pair.join("=")), - { closeAfter: true }, - ); - return; - } - onSelect([...filterValue, `${key}=${value}`], { - closeAfter: true, - }); - }} - > - - - - {key}={value} - - - - ); - })} - - ); -} - -function StatusOptions({ onSelect, value: filterValue }: OptionsProviderProps) { - return ( - - {["running", "starting", "crashed", "stopped"].map((key) => { - const isSelected = filterValue.some((val) => val === key); - return ( - { - if (isSelected) { - onSelect( - filterValue.filter( - (filterKey) => filterKey !== key, - ), - { closeAfter: true }, - ); - return; - } - - onSelect([...filterValue, key], { - closeAfter: true, - }); - }} - > - - - - ); - })} - - ); -} - -function RegionOptions({ onSelect, value: filterValue }: OptionsProviderProps) { - const regions = useAtomValue(actorRegionsAtom); - return ( - - {regions.map(({ id, name }) => { - const isSelected = filterValue.some((val) => val === id); - return ( - { - if (isSelected) { - onSelect( - filterValue.filter( - (filterKey) => filterKey !== id, - ), - { closeAfter: true }, - ); - return; - } - - onSelect([...filterValue, id], { - closeAfter: true, - }); - }} - > - - {name} - - ); - })} - - ); -} diff --git a/frontend/packages/components/src/actors/actors-sidebar-toggle-button.tsx b/frontend/packages/components/src/actors/actors-sidebar-toggle-button.tsx deleted file mode 100644 index d210653c7a..0000000000 --- a/frontend/packages/components/src/actors/actors-sidebar-toggle-button.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Button } from "@rivet-gg/components"; -import { Icon, faSidebar } from "@rivet-gg/icons"; -import { useActorsLayout } from "./actors-layout-context"; - -export function ActorsSidebarToggleButton() { - const { setFolded, isFolded } = useActorsLayout(); - - if (!isFolded) { - return null; - } - return ( -
- -
- ); -} diff --git a/frontend/packages/components/src/actors/actors-view-context-provider.tsx b/frontend/packages/components/src/actors/actors-view-context-provider.tsx deleted file mode 100644 index d7a063f1ae..0000000000 --- a/frontend/packages/components/src/actors/actors-view-context-provider.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { createContext, useContext } from "react"; - -const defaultValue = { - copy: { - createActor: "Create Actor", - createActorUsingForm: "Create Actor using simple form", - noActorsFound: "No Actors found", - selectActor: ( - <> - No Actor selected. -
{" "} - - Select an Actor from the list to view its details. - - - ), - goToActor: "Go to Actor", - showActorList: "Show Actor List", - showHiddenActors: "Show hidden Actors", - actorId: "Actor ID", - noActorsMatchFilter: "No Actors match the filters.", - noMoreActors: "No more Actors to load.", - - createActorModal: { - title: "Create Actor", - description: - "Choose a build to create an Actor from. Actor will be created using default settings.", - }, - - actorNotFound: "Actor not found", - actorNotFoundDescription: - "Change your filters to find the Actor you are looking for.", - - gettingStarted: { - title: "Getting Started with Actors", - description: - "Use a quick start guide to start deploying Actors to your environment.", - }, - }, - canCreate: true, -}; - -export const ActorsViewContext = - createContext(defaultValue); - -export const useActorsView = () => { - const context = useContext(ActorsViewContext); - if (!context) { - throw new Error( - "useActorsView must be used within a ActorsViewContextProvider", - ); - } - return context; -}; diff --git a/frontend/packages/components/src/actors/build-select.tsx b/frontend/packages/components/src/actors/build-select.tsx deleted file mode 100644 index ac5ed0eecf..0000000000 --- a/frontend/packages/components/src/actors/build-select.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { Badge, Combobox } from "@rivet-gg/components"; -import { useAtomValue } from "jotai"; -import { useMemo } from "react"; -import { actorBuildsAtom } from "./actor-context"; - -interface BuildSelectProps { - onValueChange: (value: string) => void; - value: string; - onlyCurrent?: boolean; -} - -export function BuildSelect({ - onValueChange, - value, - onlyCurrent, -}: BuildSelectProps) { - const data = useAtomValue(actorBuildsAtom); - - const builds = useMemo(() => { - let sorted = data.toSorted( - (a, b) => b.createdAt.valueOf() - a.createdAt.valueOf(), - ); - - if (onlyCurrent) { - sorted = sorted.filter((build) => build.tags.current); - } - - const findLatest = (name: string) => - sorted.find((build) => build.tags.name === name); - return sorted.map((build, index, array) => { - return { - label: ( -
-
-
- {build.tags.name || build.id.split("-")[0]} - - {findLatest(build.tags.name)?.id === - build.id ? ( - - Latest - - ) : null} -
-
- Created: {build.createdAt.toLocaleString()} -
-
-
- ), - value: build.id, - build, - }; - }); - }, [data, onlyCurrent]); - - return ( - - option.build.name.includes(search) || - option.build.tags.name.includes(search) - } - className="w-full h-14" - /> - ); -} diff --git a/frontend/packages/components/src/actors/console/actor-console-input.tsx b/frontend/packages/components/src/actors/console/actor-console-input.tsx deleted file mode 100644 index 58689575ef..0000000000 --- a/frontend/packages/components/src/actors/console/actor-console-input.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { Button, ScrollArea } from "@rivet-gg/components"; -import { useRef } from "react"; -import { useActorRpcs, useActorWorker } from "../worker/actor-worker-context"; -import { ActorConsoleMessage } from "./actor-console-message"; -import { ReplInput, type ReplInputRef, replaceCode } from "./repl-input"; - -export function ActorConsoleInput() { - const worker = useActorWorker(); - const rpcs = useActorRpcs(); - const ref = useRef(null); - - return ( -
- - - { - worker.run(code); - }} - /> - -
-
- {rpcs.map((rpc) => ( - - ))} -
-
-
-
- ); -} diff --git a/frontend/packages/components/src/actors/console/actor-console-log-formatted.tsx b/frontend/packages/components/src/actors/console/actor-console-log-formatted.tsx deleted file mode 100644 index 11545a63f5..0000000000 --- a/frontend/packages/components/src/actors/console/actor-console-log-formatted.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import type { FormattedCode } from "../worker/actor-worker-schema"; - -export function ActorConsoleLogFormatted({ tokens }: FormattedCode) { - return ( - <> - {tokens.map((tokensLine, index) => ( - - {tokensLine.map((token, index) => ( - - {token.content} - - ))} - - ))} - - ); -} diff --git a/frontend/packages/components/src/actors/console/actor-console-log.tsx b/frontend/packages/components/src/actors/console/actor-console-log.tsx deleted file mode 100644 index 38b97eb0cf..0000000000 --- a/frontend/packages/components/src/actors/console/actor-console-log.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { memo } from "react"; -import type { ReplCommand } from "../worker/actor-worker-container"; -import { ActorConsoleLogFormatted } from "./actor-console-log-formatted"; -import { ActorConsoleMessage } from "./actor-console-message"; -import { ActorObjectInspector } from "./actor-inspector"; - -type ActorConsoleLogProps = ReplCommand & { - showTimestmaps: boolean; -}; - -export const ActorConsoleLog = memo((props: ActorConsoleLogProps) => { - return ( - <> - - {"formatted" in props && props.formatted ? ( - - ) : null} - - {"error" in props ? ( - - {props.error && - typeof props.error === "object" && - "toString" in props.error - ? props.error.toString() - : JSON.stringify(props.error)} - - ) : null} - {props.logs?.map((log, index) => ( - - {log.data?.map((element, key) => { - if (typeof element === "string") { - return ( - - {element} - - ); - } - return ( - - ); - })} - - ))} - {"result" in props ? ( - - {typeof props.result === "string" ? ( - props.result - ) : ( - - )} - - ) : null} - - ); -}); diff --git a/frontend/packages/components/src/actors/console/actor-console-logs.tsx b/frontend/packages/components/src/actors/console/actor-console-logs.tsx deleted file mode 100644 index bd3593a6ba..0000000000 --- a/frontend/packages/components/src/actors/console/actor-console-logs.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { ScrollArea } from "@rivet-gg/components"; -import { useLayoutEffect, useRef } from "react"; -import { useActorDetailsSettings } from "../actor-details-settings"; -import { useActorReplCommands } from "../worker/actor-worker-context"; -import { ActorConsoleLog } from "./actor-console-log"; - -export function ActorConsoleLogs() { - const isScrolledToBottom = useRef(true); - const ref = useRef(null); - const commands = useActorReplCommands(); - - const [settings] = useActorDetailsSettings(); - - // biome-ignore lint/correctness/useExhaustiveDependencies: we want to run this effect on every commands change - useLayoutEffect(() => { - if (ref.current && isScrolledToBottom.current) { - ref.current.scrollTop = ref.current.scrollHeight; - } - }, [commands]); - - return ( - { - if (ref.current) { - isScrolledToBottom.current = - ref.current.scrollTop + ref.current.clientHeight >= - ref.current.scrollHeight - 1; - } - }, - }} - className="w-full flex-1" - > - {commands.map((log) => ( - - ))} - - ); -} diff --git a/frontend/packages/components/src/actors/console/actor-console-message.tsx b/frontend/packages/components/src/actors/console/actor-console-message.tsx deleted file mode 100644 index eaba9b1e3d..0000000000 --- a/frontend/packages/components/src/actors/console/actor-console-message.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import { cn } from "@rivet-gg/components"; -import { - Icon, - faAngleLeft, - faAngleRight, - faExclamationCircle, - faSpinnerThird, - faWarning, -} from "@rivet-gg/icons"; -import { format } from "date-fns"; -import { type ReactNode, forwardRef } from "react"; - -interface ActorConsoleMessageProps { - variant: - | "input" - | "input-pending" - | "output" - | "error" - | "log" - | "warn" - | "info" - | "debug"; - timestamp?: Date; - className?: string; - children: ReactNode; -} - -export const ActorConsoleMessage = forwardRef< - HTMLDivElement, - ActorConsoleMessageProps ->(({ children, variant, timestamp, className, ...props }, ref) => { - return ( -
-
- -
-
- {timestamp - ? format(timestamp, "LLL dd HH:mm:ss").toUpperCase() - : null} -
-
- {children} -
-
- ); -}); - -export const ConsoleMessageVariantIcon = ({ - variant, - className, -}: { - variant: string; - className?: string; -}) => { - if (variant === "input") { - return ; - } - if (variant === "input-pending") { - return ( - - ); - } - if (variant === "output") { - return ; - } - if (variant === "error") { - return ( - - ); - } - if (variant === "warn") { - return ; - } - return ; -}; - -export const getConsoleMessageVariant = (variant: string) => - cn({ - "bg-red-950/30 border-red-800/40 text-red-400 z-10": - variant === "error", - "bg-yellow-500/10 border-yellow-800/40 text-yellow-200 z-10": - variant === "warn", - "bg-blue-950/30 border-blue-800/40 text-blue-400 z-10": - variant === "debug", - }); diff --git a/frontend/packages/components/src/actors/console/actor-console.tsx b/frontend/packages/components/src/actors/console/actor-console.tsx deleted file mode 100644 index 681cbbf10d..0000000000 --- a/frontend/packages/components/src/actors/console/actor-console.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { Button } from "@rivet-gg/components"; -import { Icon, faChevronDown } from "@rivet-gg/icons"; -import { AnimatePresence, motion } from "framer-motion"; -import { useState } from "react"; -import { useActorWorkerStatus } from "../worker/actor-worker-context"; -import { ActorWorkerStatus } from "../worker/actor-worker-status"; -import { ActorConsoleInput } from "./actor-console-input"; -import { ActorConsoleLogs } from "./actor-console-logs"; - -export function ActorConsole() { - const [isOpen, setOpen] = useState(false); - - const status = useActorWorkerStatus(); - - const isBlocked = status.type !== "ready"; - - return ( - - - - {isOpen && !isBlocked ? ( - - - - - ) : null} - - - ); -} diff --git a/frontend/packages/components/src/actors/console/actor-inspector.tsx b/frontend/packages/components/src/actors/console/actor-inspector.tsx deleted file mode 100644 index 87949e7a13..0000000000 --- a/frontend/packages/components/src/actors/console/actor-inspector.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { cn } from "@rivet-gg/components"; -import type { ComponentProps } from "react"; -import { Inspector, type ObjectInspector, chromeDark } from "react-inspector"; - -const INSPECTOR_THEME = { - ...chromeDark, - BASE_BACKGROUND_COLOR: "transparent", -}; - -export function ActorObjectInspector( - props: ComponentProps, -) { - return ( -
- -
- ); -} diff --git a/frontend/packages/components/src/actors/console/repl-input.tsx b/frontend/packages/components/src/actors/console/repl-input.tsx deleted file mode 100644 index fc7ef1e372..0000000000 --- a/frontend/packages/components/src/actors/console/repl-input.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { - CodeMirror, - type CodeMirrorRef, - type CompletionContext, - EditorView, - External, - defaultKeymap, - javascript, - javascriptLanguage, - keymap, -} from "@rivet-gg/components/code-mirror"; -import { forwardRef } from "react"; - -export const replaceCode = (editor: EditorView, code: string) => { - return editor.dispatch({ - changes: { - from: 0, - to: editor.state.doc.length, - insert: code, - }, - selection: { anchor: code.length }, - scrollIntoView: true, - annotations: [External.of(true)], - }); -}; - -const deleteBgTheme = EditorView.theme({ - ".cm-content": { padding: 0 }, -}); - -export type ReplInputRef = CodeMirrorRef; - -interface ReplInputProps { - className: string; - rpcs: string[]; - onRun: (code: string) => void; -} - -export const ReplInput = forwardRef( - ({ rpcs, onRun, className }, ref) => { - const rivetKeymap = keymap.of([ - { - key: "Enter", - run: (editor) => { - onRun(editor?.state.doc.toString()); - editor.dispatch({ - changes: { - from: 0, - to: editor.state.doc.length, - insert: "", - }, - annotations: [External.of(true)], - }); - return true; - }, - }, - ...defaultKeymap, - ]); - - const replAutocomplete = javascriptLanguage.data.of({ - autocomplete: (context: CompletionContext) => { - const word = context.matchBefore(/^actor\.\w*/); - if (!word || (word?.from === word?.to && !context.explicit)) - return null; - return { - from: word.from, - to: word.to, - boost: 99, - options: rpcs.map((rpc) => ({ - label: `actor.${rpc}(/* args */)`, - apply: `actor.${rpc}(`, - validFor: /^actor\.\w*$/, - info: `Call "${rpc}" RPC on Actor`, - })), - }; - }, - }); - - return ( - - ); - }, -); diff --git a/frontend/packages/components/src/actors/create-actor-button.tsx b/frontend/packages/components/src/actors/create-actor-button.tsx deleted file mode 100644 index 3748de59bb..0000000000 --- a/frontend/packages/components/src/actors/create-actor-button.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { Button, type ButtonProps, WithTooltip } from "@rivet-gg/components"; -import { Icon, faPlus } from "@rivet-gg/icons"; -import { useNavigate } from "@tanstack/react-router"; -import { useAtomValue } from "jotai"; -import { - actorBuildsCountAtom, - actorManagerEndpointAtom, -} from "./actor-context"; -import { useActorsView } from "./actors-view-context-provider"; - -export function CreateActorButton(props: ButtonProps) { - const navigate = useNavigate(); - const builds = useAtomValue(actorBuildsCountAtom); - const endpoint = useAtomValue(actorManagerEndpointAtom); - - const { copy, canCreate: contextAllowActorsCreation } = useActorsView(); - - const canCreate = builds > 0 && contextAllowActorsCreation && endpoint; - - if (!contextAllowActorsCreation) { - return null; - } - - const content = ( -
- -
- ); - - if (canCreate) { - return content; - } - - return ( - - ); -} diff --git a/frontend/packages/components/src/actors/current-environment-version-title.tsx b/frontend/packages/components/src/actors/current-environment-version-title.tsx deleted file mode 100644 index e38d15afec..0000000000 --- a/frontend/packages/components/src/actors/current-environment-version-title.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { useSuspenseQuery } from "@tanstack/react-query"; -import { - projectEnvironmentQueryOptions, - projectVersionQueryOptions, -} from "../queries"; -import { EnvironmentVersionTitle } from "./environment-version-title"; - -interface CurrentEnvironmentVersionTitleProps { - environmentId: string; - projectId: string; -} - -export function CurrentEnvironmentVersionTitle({ - environmentId, - projectId, -}: CurrentEnvironmentVersionTitleProps) { - const { - data: { namespace: environment }, - } = useSuspenseQuery( - projectEnvironmentQueryOptions({ projectId, environmentId }), - ); - - const { data: version } = useSuspenseQuery( - projectVersionQueryOptions({ - projectId, - versionId: environment.versionId, - }), - ); - - return ( - - ); -} diff --git a/frontend/packages/components/src/actors/dialogs/create-actor-dialog.tsx b/frontend/packages/components/src/actors/dialogs/create-actor-dialog.tsx deleted file mode 100644 index 9dc8b0745f..0000000000 --- a/frontend/packages/components/src/actors/dialogs/create-actor-dialog.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { useAtomValue } from "jotai"; -import { - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from "../../ui/dialog"; -import { Flex } from "../../ui/flex"; -import { createActorAtom } from "../actor-context"; -import { useActorsView } from "../actors-view-context-provider"; -import * as ActorCreateForm from "../form/actor-create-form"; -import type { DialogContentProps } from "../hooks"; - -interface ContentProps extends DialogContentProps {} - -export default function CreateActorDialog({ onClose }: ContentProps) { - const { endpoint, create } = useAtomValue(createActorAtom); - - const { copy } = useActorsView(); - - return ( - <> - { - if (!endpoint) { - throw new Error("No endpoint"); - } - await create({ - endpoint, - id: values.buildId, - tags: Object.fromEntries( - values.tags.map((tag) => [tag.key, tag.value]), - ), - params: values.parameters - ? JSON.parse(values.parameters) - : undefined, - region: values.regionId, - }); - onClose?.(); - }} - defaultValues={{ buildId: "", regionId: "" }} - > - - {copy.createActorModal.title} - - {copy.createActorModal.description} - - - - - - - {/* */} - - - - Create - - - - - ); -} diff --git a/frontend/packages/components/src/actors/dialogs/go-to-actor-dialog.tsx b/frontend/packages/components/src/actors/dialogs/go-to-actor-dialog.tsx deleted file mode 100644 index 3baf32d185..0000000000 --- a/frontend/packages/components/src/actors/dialogs/go-to-actor-dialog.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { Button } from "../../ui/button"; -import { DialogFooter, DialogHeader, DialogTitle } from "../../ui/dialog"; -import { useActorsView } from "../actors-view-context-provider"; -import * as GoToActorForm from "../form/go-to-actor-form"; -import type { DialogContentProps } from "../hooks"; - -interface ContentProps extends DialogContentProps { - onSubmit?: (actorId: string) => void; -} - -export default function GoToActorDialogContent({ - onClose, - onSubmit, -}: ContentProps) { - const { copy } = useActorsView(); - return ( - { - onSubmit?.(actorId); - }} - > - - {copy.goToActor} - - - - - Go - - - ); -} diff --git a/frontend/packages/components/src/actors/dynamic-servers-feature-card.tsx b/frontend/packages/components/src/actors/dynamic-servers-feature-card.tsx deleted file mode 100644 index 7df263034b..0000000000 --- a/frontend/packages/components/src/actors/dynamic-servers-feature-card.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { - Card, - CardContent, - CardHeader, - CardTitle, - Link, - Text, -} from "@rivet-gg/components"; -import { Link as RouterLink } from "@tanstack/react-router"; - -export function DynamicServersFeatureCard() { - return ( - - - Legacy Lobbies and Environments - - - - Dynamic servers and builds are the new way to manage your - project servers. However, if you're looking for lobbies and - namespaces, you can switch back to the old interface in the{" "} - - - User Settings - - - . - - - - ); -} diff --git a/frontend/packages/components/src/actors/environment-select.tsx b/frontend/packages/components/src/actors/environment-select.tsx deleted file mode 100644 index 2261946dbc..0000000000 --- a/frontend/packages/components/src/actors/environment-select.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { projectEnvironmentsQueryOptions } from "@/domains/project/queries"; -import { - Flex, - Select, - SelectContent, - SelectItem, - SelectSeparator, - SelectTrigger, - SelectValue, -} from "@rivet-gg/components"; -import { Icon, faCirclePlus } from "@rivet-gg/icons"; -import { useSuspenseQuery } from "@tanstack/react-query"; -import { type ComponentProps, useCallback } from "react"; - -interface EnvironmentSelectProps extends ComponentProps { - projectId: string; - showCreateEnvironment?: boolean; - onCreateClick?: () => void; - variant?: ComponentProps["variant"]; -} - -export function EnvironmentSelect({ - showCreateEnvironment, - onCreateClick, - onValueChange, - projectId, - variant, - ...props -}: EnvironmentSelectProps) { - const { data } = useSuspenseQuery( - projectEnvironmentsQueryOptions(projectId), - ); - - const handleValueChange = useCallback( - (value: string) => { - if (value === "create") { - onCreateClick?.(); - return; - } - onValueChange?.(value); - }, - [onCreateClick, onValueChange], - ); - - return ( - - ); -} diff --git a/frontend/packages/components/src/actors/environment-version-title.tsx b/frontend/packages/components/src/actors/environment-version-title.tsx deleted file mode 100644 index 0777d81cda..0000000000 --- a/frontend/packages/components/src/actors/environment-version-title.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { Badge, Flex } from "@rivet-gg/components"; - -interface EnvironmentVersionTitleProps { - environment: string; - version: string; -} - -export function EnvironmentVersionTitle({ - environment, - version, -}: EnvironmentVersionTitleProps) { - return ( - - {environment} - {version} - - ); -} diff --git a/frontend/packages/components/src/actors/form/actor-create-form.tsx b/frontend/packages/components/src/actors/form/actor-create-form.tsx deleted file mode 100644 index bf92b99b46..0000000000 --- a/frontend/packages/components/src/actors/form/actor-create-form.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import { - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, - Label, - createSchemaForm, -} from "@rivet-gg/components"; -import { JsonCode } from "@rivet-gg/components/code-mirror"; -import { type UseFormReturn, useFormContext } from "react-hook-form"; -import z from "zod"; - -import { useAtomValue, useSetAtom } from "jotai"; -import { - actorCustomTagKeys, - actorCustomTagValues, - actorTagKeysAtom, - actorTagValuesAtom, -} from "../actor-context"; -import { BuildSelect } from "../build-select"; -import { RegionSelect } from "../region-select"; -import { - Tags as TagsInput, - formSchema as tagsFormSchema, -} from "./build-tags-form"; - -const jsonValid = z.custom((value) => { - try { - JSON.parse(value); - return true; - } catch { - return false; - } -}); - -export const formSchema = z.object({ - buildId: z.string().nonempty("Build is required"), - regionId: z.string(), - parameters: jsonValid.optional(), - tags: tagsFormSchema.shape.tags, -}); - -export type FormValues = z.infer; -export type SubmitHandler = ( - values: FormValues, - form: UseFormReturn, -) => Promise; - -const { Form, Submit } = createSchemaForm(formSchema); -export { Form, Submit }; - -export const Build = () => { - const { control } = useFormContext(); - return ( - ( - - Build - - - - - - )} - /> - ); -}; - -export const Region = () => { - const { control } = useFormContext(); - - return ( - ( - - Region - - - - - - )} - /> - ); -}; - -export const Parameters = () => { - const { control } = useFormContext(); - return ( - ( - - Parameters - - - - - - )} - /> - ); -}; - -export const Tags = () => { - const setValues = useSetAtom(actorCustomTagValues); - const setKeys = useSetAtom(actorCustomTagKeys); - - const keys = useAtomValue(actorTagKeysAtom); - const values = useAtomValue(actorTagValuesAtom); - - return ( -
- - ({ - label: key, - value: key, - }))} - values={values.map((value) => ({ - label: value, - value: value, - }))} - onCreateKeyOption={(value) => { - setKeys((old) => - Array.from(new Set([...old, value]).values()), - ); - }} - onCreateValueOption={(value) => { - setValues((old) => - Array.from(new Set([...old, value]).values()), - ); - }} - /> -
- ); -}; diff --git a/frontend/packages/components/src/actors/form/build-tags-form.tsx b/frontend/packages/components/src/actors/form/build-tags-form.tsx deleted file mode 100644 index b431baf7cd..0000000000 --- a/frontend/packages/components/src/actors/form/build-tags-form.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import { Icon, faTrash } from "@rivet-gg/icons"; -import { - type UseFormReturn, - useFieldArray, - useFormContext, -} from "react-hook-form"; -import z from "zod"; -import { createSchemaForm } from "../../lib/create-schema-form"; -import { Button } from "../../ui/button"; -import { Combobox, type ComboboxOption as Option } from "../../ui/combobox"; -import { - FormControl, - FormFieldContext, - FormItem, - FormLabel, - FormMessage, -} from "../../ui/form"; -import { Text } from "../../ui/typography"; - -export const formSchema = z.object({ - tags: z - .array( - z.object({ - key: z.string().min(1), - value: z.string(), - }), - ) - .superRefine((tags, ctx) => { - const tagsSet = new Set(); - for (const [idx, tag] of [...tags].reverse().entries()) { - if (tagsSet.has(tag.key)) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - path: [idx, "key"], - message: "Tag keys must be unique", - }); - } - tagsSet.add(tag.key); - } - }), -}); - -export type FormValues = z.infer; -export type SubmitHandler = ( - values: FormValues, - form: UseFormReturn, -) => Promise; - -const { Form, Submit } = createSchemaForm(formSchema); -export { Form, Submit }; - -export const Tags = ({ - onCreateKeyOption, - onCreateValueOption, - keys, - values, -}: { - onCreateKeyOption: (option: string) => void; - onCreateValueOption: (option: string) => void; - keys: Option[]; - values: Option[]; -}) => { - const { control, setValue, watch } = useFormContext(); - const { fields, append, remove } = useFieldArray({ - name: "tags", - control, - }); - - return ( - <> - {fields.length === 0 ? ( - - There's no tags added. - - ) : null} - {fields.map((field, index) => ( -
- - - Key - - { - setValue(`tags.${index}.key`, value, { - shouldDirty: true, - shouldTouch: true, - shouldValidate: true, - }); - }} - allowCreate - onCreateOption={onCreateKeyOption} - /> - - - - - - - - Value - - { - setValue(`tags.${index}.value`, value, { - shouldDirty: true, - shouldTouch: true, - shouldValidate: true, - }); - }} - allowCreate - onCreateOption={onCreateValueOption} - /> - - - - - -
- ))} - - - ); -}; diff --git a/frontend/packages/components/src/actors/form/go-to-actor-form.tsx b/frontend/packages/components/src/actors/form/go-to-actor-form.tsx deleted file mode 100644 index 4b7acdf0d2..0000000000 --- a/frontend/packages/components/src/actors/form/go-to-actor-form.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { type UseFormReturn, useFormContext } from "react-hook-form"; -import z from "zod"; -import { createSchemaForm } from "../../lib/create-schema-form"; -import { - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "../../ui/form"; -import { Input } from "../../ui/input"; -import { useActorsView } from "../actors-view-context-provider"; - -export const formSchema = z.object({ - actorId: z.string().nonempty("Actor ID is required").uuid(), -}); - -export type FormValues = z.infer; -export type SubmitHandler = ( - values: FormValues, - form: UseFormReturn, -) => Promise; - -const { Form, Submit } = createSchemaForm(formSchema); -export { Form, Submit }; - -export const ActorId = () => { - const { control } = useFormContext(); - const { copy } = useActorsView(); - return ( - ( - - {copy.actorId} - - - - - - )} - /> - ); -}; diff --git a/frontend/packages/components/src/actors/get-started.tsx b/frontend/packages/components/src/actors/get-started.tsx deleted file mode 100644 index 8039cb19f9..0000000000 --- a/frontend/packages/components/src/actors/get-started.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { Icon, faActors, faFunction, faServer } from "@rivet-gg/icons"; -import { motion } from "framer-motion"; -import type { ComponentProps } from "react"; -import { DocsSheet } from "../docs-sheet"; -import { cn } from "../lib/utils"; -import { Button } from "../ui/button"; - -export function ActorsResources() { - return ( - <> -
- - - -
- - ); -} - -const linkVariants = { - hidden: { - opacity: 0, - }, - show: { - opacity: 1, - }, -}; - -interface ExampleLinkProps { - title: string; - description?: string; - icon: ComponentProps["icon"]; - href: string; - size?: "sm" | "md" | "lg"; -} - -function ExampleLink({ - title, - description, - icon, - href, - size = "lg", -}: ExampleLinkProps) { - return ( - - - - ); -} diff --git a/frontend/packages/components/src/actors/getting-started.tsx b/frontend/packages/components/src/actors/getting-started.tsx deleted file mode 100644 index e64f8ac502..0000000000 --- a/frontend/packages/components/src/actors/getting-started.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { Icon, faActors } from "@rivet-gg/icons"; -import { useActorsView } from "./actors-view-context-provider"; -import { ActorsResources } from "./get-started"; - -export function GettingStarted() { - const { copy } = useActorsView(); - return ( -
-
- -

- {copy.gettingStarted.title} -

-

- {copy.gettingStarted.description} -

-
- -
- ); -} diff --git a/frontend/packages/components/src/actors/go-to-actor-button.tsx b/frontend/packages/components/src/actors/go-to-actor-button.tsx deleted file mode 100644 index b38d67f676..0000000000 --- a/frontend/packages/components/src/actors/go-to-actor-button.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { Button, type ButtonProps } from "@rivet-gg/components"; -import { Icon, faMagnifyingGlass } from "@rivet-gg/icons"; -import { useNavigate } from "@tanstack/react-router"; -import { useActorsView } from "./actors-view-context-provider"; - -export function GoToActorButton(props: ButtonProps) { - const navigate = useNavigate(); - const { copy } = useActorsView(); - return ( - - ); -} diff --git a/frontend/packages/components/src/actors/group-project-select.tsx b/frontend/packages/components/src/actors/group-project-select.tsx deleted file mode 100644 index d1f7fa6c86..0000000000 --- a/frontend/packages/components/src/actors/group-project-select.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { groupProjectsQueryOptions } from "@/domains/project/queries"; -import { - Flex, - Select, - SelectContent, - SelectItem, - SelectSeparator, - SelectTrigger, - SelectValue, -} from "@rivet-gg/components"; -import { Icon, faCirclePlus } from "@rivet-gg/icons"; -import { useSuspenseQuery } from "@tanstack/react-query"; -import { type ComponentProps, useCallback } from "react"; - -interface GroupProjectSelectProps extends ComponentProps { - groupId: string; - showCreateProject?: boolean; - onCreateClick?: () => void; - variant?: ComponentProps["variant"]; -} - -export function GroupProjectSelect({ - groupId, - showCreateProject, - onCreateClick, - onValueChange, - variant, - ...props -}: GroupProjectSelectProps) { - const { data } = useSuspenseQuery(groupProjectsQueryOptions(groupId)); - - const handleValueChange = useCallback( - (value: string) => { - if (value === "create") { - onCreateClick?.(); - return; - } - onValueChange?.(value); - }, - [onCreateClick, onValueChange], - ); - - return ( - - ); -} diff --git a/frontend/packages/components/src/actors/hooks/index.ts b/frontend/packages/components/src/actors/hooks/index.ts deleted file mode 100644 index 14c4d0400f..0000000000 --- a/frontend/packages/components/src/actors/hooks/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./use-dialog"; diff --git a/frontend/packages/components/src/actors/hooks/use-dialog.tsx b/frontend/packages/components/src/actors/hooks/use-dialog.tsx deleted file mode 100644 index 77373da6c4..0000000000 --- a/frontend/packages/components/src/actors/hooks/use-dialog.tsx +++ /dev/null @@ -1,159 +0,0 @@ -"use client"; -import { - type ComponentProps, - type ComponentType, - lazy, - useCallback, - useMemo, - useState, -} from "react"; -import { Dialog, DialogContent, type DialogProps } from "../../ui/dialog"; - -export interface DialogContentProps { - onClose?: () => void; -} - -interface DialogConfig { - autoFocus?: boolean; -} - -export const createDialogHook = < - // biome-ignore lint/suspicious/noExplicitAny: we don't know the type of the component, so we use any - Component extends Promise<{ default: ComponentType }>, ->( - component: Component, - opts: DialogConfig = {}, -) => { - const DialogImpl = ({ - dialogProps, - ...props - }: ComponentProps["default"]> & { - dialogProps?: DialogProps; - }) => { - // biome-ignore lint/correctness/useExhaustiveDependencies: component here is a static value, won't change over time - const Content = useMemo(() => lazy(() => component), []); - - return ( - - { - if (opts.autoFocus === false) { - return e.preventDefault(); - } - }} - > - dialogProps?.onOpenChange?.(false)} - /> - - - ); - }; - - const useHook = (props: ComponentProps["default"]>) => { - const [isOpen, setIsOpen] = useState(() => false); - - const close = useCallback(() => { - setIsOpen(false); - }, []); - - const open = useCallback(() => { - setIsOpen(true); - }, []); - - const handleOpenChange = useCallback((open: boolean) => { - setIsOpen(open); - }, []); - - return { - open, - close, - dialog: ( - - ), - }; - }; - - useHook.Dialog = DialogImpl; - - return useHook; -}; - -export const createDataDialogHook = < - const DataPropKeys extends string[], - // biome-ignore lint/suspicious/noExplicitAny: we don't know the type of the component, so we use any - Component extends Promise<{ default: ComponentType }>, ->( - _: DataPropKeys, - component: Component, - opts: DialogConfig = {}, -) => { - return ( - props: Omit< - ComponentProps["default"]>, - DataPropKeys[number] - >, - ) => { - const [isOpen, setIsOpen] = useState(false); - const [data, setData] = - useState< - Pick< - ComponentProps["default"]>, - DataPropKeys[number] - > - >(); - - const close = useCallback(() => { - setIsOpen(false); - }, []); - - const open = useCallback( - ( - data: Pick< - ComponentProps["default"]>, - DataPropKeys[number] - >, - ) => { - setIsOpen(true); - setData(data); - }, - [], - ); - - // biome-ignore lint/correctness/useExhaustiveDependencies: component here is a static value, won't change over time - const Content = useMemo(() => lazy(() => component), []); - - return { - open, - dialog: ( - - { - if (opts.autoFocus === false) { - return e.preventDefault(); - } - }} - > - - - - ), - }; - }; -}; - -export function useDialog() {} - -useDialog.GoToActor = createDialogHook(import("../dialogs/go-to-actor-dialog")); - -useDialog.Feedback = createDialogHook(import("../../dialogs/feedback-dialog")); -useDialog.CreateActor = createDialogHook( - import("../dialogs/create-actor-dialog"), -); diff --git a/frontend/packages/components/src/actors/index.tsx b/frontend/packages/components/src/actors/index.tsx deleted file mode 100644 index dd5baf45d2..0000000000 --- a/frontend/packages/components/src/actors/index.tsx +++ /dev/null @@ -1,17 +0,0 @@ -export * from "./getting-started"; -export * from "./actors-list-preview"; -export * from "./actor-tags"; -export * from "./actor-context"; -export * from "./actors-actor-details"; -export * from "./hooks/index"; -export { getActorStatus } from "./actor-status-indicator"; -export * from "./actors-layout"; -export * from "./actors-layout-context"; -export * from "./console/actor-console-message"; -export * from "./actor-region"; -export * from "./console/actor-inspector"; -export * from "./actor-status-indicator"; -export * from "./actor-status-label"; -export * from "./actors-view-context-provider"; -export * from "./actor-not-found"; -export { ActorsListFiltersSchema, pickActorListFilters } from "./actors-list"; diff --git a/frontend/packages/components/src/actors/matchmaker-lobby-config-settings-card.tsx b/frontend/packages/components/src/actors/matchmaker-lobby-config-settings-card.tsx deleted file mode 100644 index c6112dea98..0000000000 --- a/frontend/packages/components/src/actors/matchmaker-lobby-config-settings-card.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import * as MatchmakerLobbyConfigForm from "@/domains/project/forms/matchmaker-lobby-config-form"; -import { - Card, - CardContent, - CardFooter, - CardHeader, - CardTitle, - Flex, -} from "@rivet-gg/components"; -import { useSuspenseQuery } from "@tanstack/react-query"; -import { useMatchmakerLobbyConfigFormHandler } from "../hooks/use-matchmaker-lobby-config-form-handler"; -import { projectEnvironmentQueryOptions } from "../queries"; - -interface MatchMakerLobbyConfigSettingsCardProps { - projectId: string; - environmentId: string; -} - -export function MatchMakerLobbyConfigSettingsCard({ - environmentId, - projectId, -}: MatchMakerLobbyConfigSettingsCardProps) { - const { data } = useSuspenseQuery( - projectEnvironmentQueryOptions({ projectId, environmentId }), - ); - - const handleSubmit = useMatchmakerLobbyConfigFormHandler({ - environmentId, - projectId, - }); - - return ( - - - - Config - - - - - - - - - - Save - - - - - ); -} diff --git a/frontend/packages/components/src/actors/project-builds-table-actions.tsx b/frontend/packages/components/src/actors/project-builds-table-actions.tsx deleted file mode 100644 index 47dcea2344..0000000000 --- a/frontend/packages/components/src/actors/project-builds-table-actions.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { - Button, - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from "@rivet-gg/components"; -import { Icon, faEllipsisH } from "@rivet-gg/icons"; -import { useNavigate } from "@tanstack/react-router"; - -interface ProjectBuildsTableActionsProps { - buildId: string; -} - -export function ProjectBuildsTableActions({ - buildId, -}: ProjectBuildsTableActionsProps) { - const navigate = useNavigate(); - return ( - - - - - - { - navigate({ - to: ".", - search: { modal: "edit-tags", buildId }, - }); - }} - > - Edit tags - - - - ); -} diff --git a/frontend/packages/components/src/actors/project-environments-table-actions.tsx b/frontend/packages/components/src/actors/project-environments-table-actions.tsx deleted file mode 100644 index b6722a6274..0000000000 --- a/frontend/packages/components/src/actors/project-environments-table-actions.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { - Button, - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from "@rivet-gg/components"; -import { Icon, faEllipsisH } from "@rivet-gg/icons"; - -export function ProjectEnvironmentsTableActions() { - return ( - - - - - - Manage - - - ); -} diff --git a/frontend/packages/components/src/actors/project-logo-settings-card.tsx b/frontend/packages/components/src/actors/project-logo-settings-card.tsx deleted file mode 100644 index e4ede8afde..0000000000 --- a/frontend/packages/components/src/actors/project-logo-settings-card.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import * as GroupImageForm from "@/domains/project/forms/project-logo-form"; -import { - Card, - CardContent, - CardFooter, - CardHeader, - CardTitle, -} from "@rivet-gg/components"; -import { useProjectLogoUploadMutation } from "../queries"; - -interface ProjectLogoSettingsCardProps { - projectId: string; -} - -export function ProjectLogoSettingsCard({ - projectId, -}: ProjectLogoSettingsCardProps) { - const { mutateAsync } = useProjectLogoUploadMutation(projectId); - return ( - { - try { - await mutateAsync({ file: values.logo }); - } catch { - form.setError("logo", { - type: "manual", - message: "An error occurred while uploading the image", - }); - } - }} - defaultValues={{ logo: undefined }} - > - - - Project Logo - - - - - - Save - - - - ); -} diff --git a/frontend/packages/components/src/actors/project-select.tsx b/frontend/packages/components/src/actors/project-select.tsx deleted file mode 100644 index 1438de3313..0000000000 --- a/frontend/packages/components/src/actors/project-select.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { GroupAvatar } from "@/domains/group/components/group-avatar"; -import { projectsByGroupQueryOptions } from "@/domains/project/queries"; -import { - Flex, - Select, - SelectContent, - SelectGroup, - SelectItem, - SelectLabel, - SelectSeparator, - SelectTrigger, - SelectValue, -} from "@rivet-gg/components"; -import { Icon, faCirclePlus } from "@rivet-gg/icons"; -import { useSuspenseQuery } from "@tanstack/react-query"; -import { type ComponentProps, Fragment, useCallback } from "react"; - -interface ProjectSelectProps extends ComponentProps { - showCreateProject?: boolean; - onCreateClick?: () => void; -} - -export function ProjectSelect({ - showCreateProject, - onCreateClick, - onValueChange, - ...props -}: ProjectSelectProps) { - const { data } = useSuspenseQuery(projectsByGroupQueryOptions()); - - const handleValueChange = useCallback( - (value: string) => { - if (value === "create") { - onCreateClick?.(); - return; - } - onValueChange?.(value); - }, - [onCreateClick, onValueChange], - ); - - return ( - - ); -} diff --git a/frontend/packages/components/src/actors/project-table-actions.tsx b/frontend/packages/components/src/actors/project-table-actions.tsx deleted file mode 100644 index bd462c5d44..0000000000 --- a/frontend/packages/components/src/actors/project-table-actions.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { - Button, - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from "@rivet-gg/components"; -import { Icon, faEllipsisH } from "@rivet-gg/icons"; - -export function ProjectTableActions() { - return ( - - - - - - Manage - - - ); -} diff --git a/frontend/packages/components/src/actors/project-tile.tsx b/frontend/packages/components/src/actors/project-tile.tsx deleted file mode 100644 index 0938f22eaa..0000000000 --- a/frontend/packages/components/src/actors/project-tile.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import type { Rivet } from "@rivet-gg/api"; -import { AssetImage, Flex, Text } from "@rivet-gg/components"; -import { BillingPlanBadge } from "./billing/billing-plan-badge"; - -interface ProjectTileProps - extends Pick< - Rivet.game.GameSummary, - "gameId" | "displayName" | "logoUrl" - > {} - -export function ProjectTile({ - gameId: projectId, - displayName, - logoUrl, -}: ProjectTileProps) { - return ( - -
- -
- {displayName} - -
- ); -} diff --git a/frontend/packages/components/src/actors/region-select.tsx b/frontend/packages/components/src/actors/region-select.tsx deleted file mode 100644 index d88d187105..0000000000 --- a/frontend/packages/components/src/actors/region-select.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { Combobox } from "@rivet-gg/components"; -import { useAtomValue } from "jotai"; -import { actorRegionsAtom } from "./actor-context"; -import { ActorRegion } from "./actor-region"; - -interface RegionSelectProps { - onValueChange: (value: string) => void; - value: string; -} - -export function RegionSelect({ onValueChange, value }: RegionSelectProps) { - const data = useAtomValue(actorRegionsAtom); - - const regions = [ - { - label: "Automatic (Recommended)", - value: "", - region: { id: "", name: "Automatic (Recommended)" }, - }, - ...data.map((region) => { - return { - label: , - value: region.id, - region, - }; - }), - ]; - - return ( - { - const search = searchMixed.toLowerCase(); - return ( - option.region.id.includes(search) || - option.region.name.includes(search) - ); - }} - className="w-full" - /> - ); -} diff --git a/frontend/packages/components/src/actors/worker/actor-repl.worker.ts b/frontend/packages/components/src/actors/worker/actor-repl.worker.ts deleted file mode 100644 index 98753fabf2..0000000000 --- a/frontend/packages/components/src/actors/worker/actor-repl.worker.ts +++ /dev/null @@ -1,354 +0,0 @@ -import { - type InspectData, - type ToClient, - ToClientSchema, - type ToServer, -} from "actor-core/inspector/protocol/actor"; -import type { Request, ResponseOk } from "actor-core/protocol/http"; -import { fromJs } from "esast-util-from-js"; -import { toJs } from "estree-util-to-js"; -import { - createHighlighterCore, - createOnigurumaEngine, - type HighlighterCore, -} from "shiki"; -import { endWithSlash } from "../../lib/utils"; -import { - MessageSchema, - type ReplErrorCode, - type Response, - ResponseSchema, -} from "./actor-worker-schema"; - -class ReplError extends Error { - constructor( - public readonly code: ReplErrorCode, - message: string, - ) { - super(message); - } - - static unsupported() { - return new ReplError("unsupported", "Actor unsupported"); - } -} - -export let highlighter: HighlighterCore | undefined; - -async function formatCode(code: string) { - highlighter ??= await createHighlighterCore({ - themes: [import("shiki/themes/github-dark-default.mjs")], - langs: [import("@shikijs/langs/typescript")], - engine: createOnigurumaEngine(import("shiki/wasm")), - }); - - return highlighter.codeToTokens(code, { - lang: "typescript", - theme: "github-dark-default", - }); -} - -const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); - -async function evaluateCode(code: string, args: Record) { - const argsString = Object.keys(args); - const argValues = Object.values(args); - - let jsCode: ReturnType; - try { - const program = fromJs(code, { - module: true, - allowAwaitOutsideFunction: true, - allowReturnOutsideFunction: true, - }); - - const lastStatement = program.body[program.body.length - 1]; - if (lastStatement.type === "ExpressionStatement") { - program.body[program.body.length - 1] = { - type: "ReturnStatement", - argument: lastStatement.expression, - }; - } - - jsCode = toJs(program); - } catch (e) { - throw new ReplError("syntax", "Syntax error"); - } - - return new Function( - "window", - ...argsString, - `"use strict"; - return (async () => { - ${jsCode.value} - })() - `, - )({}, ...argValues); -} - -const createConsole = (id: string) => { - return new Proxy( - { ...console }, - { - get(target, prop) { - return (...args: unknown[]) => { - respond({ - type: "log", - id, - data: { - method: prop as "log" | "warn" | "error", - data: args, - timestamp: new Date().toISOString(), - }, - }); - return Reflect.get(target, prop)(...args); - }; - }, - }, - ); -}; - -let init: null | ({ ws: WebSocket; url: URL } & InspectData) = null; - -async function connect(endpoint: string, opts?: { token?: string }) { - const url = new URL("inspect", endWithSlash(endpoint)); - - if (opts?.token) { - url.searchParams.set("token", opts.token); - } - - const ws = new WebSocket(url); - - await waitForOpen(ws); - - ws.send( - JSON.stringify({ - type: "info", - } satisfies ToServer), - ); - - const { type: _, ...info } = await waitForMessage(ws, "info"); - init = { ...info, ws, url: new URL(endpoint) }; - - ws.addEventListener("message", (event) => { - try { - const data = ToClientSchema.parse(JSON.parse(event.data)); - - if (data.type === "info") { - return respond({ - type: "inspect", - data: { - ...data, - }, - }); - } - if (data.type === "error") { - return respond({ - type: "error", - data: data.message, - }); - } - } catch (error) { - console.warn("Malformed message", event.data, error); - return; - } - }); - - ws.addEventListener("close", () => { - respond({ - type: "lost-connection", - }); - setTimeout(() => { - connect(endpoint, opts); - }, 500); - }); - - respond({ - type: "ready", - data: { - ...info, - }, - }); -} - -addEventListener("message", async (event) => { - const { success, error, data } = MessageSchema.safeParse(event.data); - - if (!success) { - console.error("Malformed message", event.data, error); - return; - } - - if (data.type === "init") { - if (init) { - respond({ - type: "error", - data: new Error("Actor already initialized"), - }); - return; - } - - try { - await Promise.race([ - connect(data.endpoint, data.token ? { token: data.token } : {}), - wait(5000).then(() => { - throw new Error("Timeout"); - }), - ]); - - return; - } catch (e) { - return respond({ - type: "error", - data: e, - }); - } - } - - if (data.type === "code") { - const actor = init; - if (!actor) { - respond({ - type: "error", - data: new Error("Actor not initialized"), - }); - return; - } - - try { - const formatted = await formatCode(data.data); - respond({ - type: "formatted", - id: data.id, - data: formatted, - }); - - const createRpc = - (rpc: string) => - async (...args: unknown[]) => { - const url = new URL( - `rpc/${rpc}`, - endWithSlash(actor.url.href), - ); - const response = await fetch(url, { - method: "POST", - body: JSON.stringify({ - a: args, - } satisfies Request), - }); - - if (!response.ok) { - throw new Error("RPC failed"); - } - - const data = (await response.json()) as ResponseOk; - return data.o; - }; - - const exposedActor = Object.fromEntries( - init?.rpcs.map((rpc) => [rpc, createRpc(rpc)]) ?? [], - ); - - const evaluated = await evaluateCode(data.data, { - console: createConsole(data.id), - wait, - actor: exposedActor, - }); - return respond({ - type: "result", - id: data.id, - data: evaluated, - }); - } catch (e) { - return respond({ - type: "error", - id: data.id, - data: e, - }); - } - } - - if (data.type === "set-state") { - const actor = init; - if (!actor) { - respond({ - type: "error", - data: new Error("Actor not initialized"), - }); - return; - } - - try { - const state = JSON.parse(data.data); - actor.ws.send( - JSON.stringify({ - type: "setState", - state, - } satisfies ToServer), - ); - } catch (e) { - return respond({ - type: "error", - data: e, - }); - } - } -}); - -function respond(msg: Response) { - return postMessage(ResponseSchema.parse(msg)); -} - -function waitForOpen(ws: WebSocket) { - const { promise, resolve, reject } = Promise.withResolvers(); - ws.addEventListener("open", () => { - resolve(undefined); - }); - ws.addEventListener("error", (event) => { - reject(); - }); - ws.addEventListener("close", (event) => { - reject(); - }); - - return Promise.race([ - promise, - wait(5000).then(() => { - throw new Error("Timeout"); - }), - ]); -} - -function waitForMessage( - ws: WebSocket, - type: T, -): Promise> { - const { promise, resolve, reject } = - Promise.withResolvers>(); - - function onMessage(event: MessageEvent) { - try { - const data = ToClientSchema.parse(JSON.parse(event.data)); - - if (data.type === type) { - resolve(data as Extract); - ws.removeEventListener("message", onMessage); - } - } catch (e) { - console.error(e); - } - } - - ws.addEventListener("message", onMessage); - ws.addEventListener("error", (event) => { - ws.removeEventListener("message", onMessage); - reject(); - }); - - return Promise.race([ - promise, - wait(5000).then(() => { - throw new Error("Timeout"); - }), - ]); -} diff --git a/frontend/packages/components/src/actors/worker/actor-worker-container.ts b/frontend/packages/components/src/actors/worker/actor-worker-container.ts deleted file mode 100644 index 7352e8e6bc..0000000000 --- a/frontend/packages/components/src/actors/worker/actor-worker-container.ts +++ /dev/null @@ -1,330 +0,0 @@ -import { CancelledError } from "@tanstack/react-query"; -import { toast } from "sonner"; -import { ls } from "../../lib/utils"; -import ActorWorker from "./actor-repl.worker?worker"; -import { - type CodeMessage, - type FormattedCode, - type InitMessage, - type InspectData, - type Log, - ResponseSchema, - type SetStateMessage, -} from "./actor-worker-schema"; - -export type ReplCommand = { - logs: Log[]; - code: string; - key: string; - inputTimestamp?: string; - outputTimestamp?: string; -} & ( - | { status: "pending" } - | { status: "formatted"; formatted: FormattedCode } - | { status: "success"; formatted: FormattedCode; result: unknown } - | { status: "error"; formatted: FormattedCode | undefined; error: unknown } -); - -export type ContainerStatus = - | { type: "ready" } - | { type: "error"; error: unknown } - | { type: "pending" } - | { type: "unsupported"; error: unknown } - | { type: "unknown" }; - -export type ContainerState = { - status: ContainerStatus; - commands: ReplCommand[]; -} & InspectData; - -export class ActorWorkerContainer { - #state: ContainerState = { - status: { type: "unknown" }, - commands: [], - rpcs: [], - state: { enabled: false, value: undefined }, - connections: [], - }; - - #meta: { - actorId: string; - } | null = null; - - #opts: { - notifyOnReconnect?: boolean; - } | null = null; - - #listeners: (() => void)[] = []; - #worker: Worker | undefined; - - // - async init({ - actorId, - endpoint, - signal, - notifyOnReconnect, - }: { - actorId: string; - endpoint: string; - signal: AbortSignal; - notifyOnReconnect?: boolean; - }) { - this.terminate(); - - this.#meta = { actorId }; - this.#opts = { notifyOnReconnect }; - this.#state.status = { type: "pending" }; - this.#update(); - try { - signal.throwIfAborted(); - - // FIXME(RVT-4553) - // if (actor.resources.cpu !== 125 || actor.resources.memory !== 128) { - // throw new Error("Unsupported actor resources"); - // } - - // If we reached this point, the actor is supported - // check if we still operate on the same actor - if (this.#meta.actorId !== actorId) { - // if not, we don't need to do anything - return null; - } - - const worker = new ActorWorker({ name: `actor-${actorId}` }); - signal.throwIfAborted(); - // now worker needs to check if the actor is supported - this.#setupWorker(worker, { actorId, endpoint }); - signal.throwIfAborted(); - return worker; - } catch (e) { - console.log(e); - // If we reached this point, the actor is not supported - // check if we still operate on the same actor - if (e instanceof DOMException && e.name === "AbortError") { - return null; - } - - if (e instanceof CancelledError) { - this.#worker?.terminate(); - this.#worker = undefined; - return null; - } - - this.#worker?.terminate(); - this.#worker = undefined; - this.#state.status = { type: "unsupported", error: e }; - this.#update(); - } - return null; - } - - terminate() { - this.#worker?.terminate(); - this.#worker = undefined; - this.#state.commands = []; - this.#state.status = { type: "unknown" }; - this.#state.rpcs = []; - this.#state.state = { - enabled: false, - value: undefined, - }; - this.#meta = null; - this.#opts = null; - this.#state.connections = []; - this.#update(); - } - - #setupWorker(worker: Worker, data: Omit) { - this.#worker = worker; - this.#worker.addEventListener("message", (event) => { - try { - this.#handleMessage(event); - } catch (e) { - console.error(e); - this.#state.status = { type: "error", error: e }; - this.#update(); - } - }); - - this.#worker.addEventListener("error", (error) => { - console.log(error, error.message, error.error); - }); - - this.#worker.postMessage({ - type: "init", - ...data, - token: ls.get("rivet-token")?.token, - } satisfies InitMessage); - } - - run(data: string) { - const key = Date.now().toString(); - this.#state.commands = [ - ...this.#state.commands, - { status: "pending", code: data, key, logs: [] }, - ]; - - this.#worker?.postMessage({ - type: "code", - data, - id: key, - } satisfies CodeMessage); - this.#update(); - } - - setState(data: string) { - this.#worker?.postMessage({ - type: "set-state", - data, - } satisfies SetStateMessage); - this.#state.state = { - ...this.#state.state, - value: JSON.parse(data || "{}"), - }; - this.#update(); - } - - getCommands() { - return this.#state.commands; - } - - getStatus() { - return this.#state.status; - } - - getRpcs() { - return this.#state.rpcs; - } - - getState() { - return this.#state.state; - } - - getConnections() { - return this.#state.connections; - } - - subscribe(cb: () => void) { - this.#listeners.push(cb); - return () => { - this.#listeners = this.#listeners.filter( - (listener) => listener !== cb, - ); - }; - } - - #handleMessage(event: MessageEvent) { - const { success, data: msg } = ResponseSchema.safeParse(event.data); - - if (!success) { - return; - } - - if (msg.type === "formatted") { - const command = this.#state.commands.find( - (command) => command.key === msg.id, - ); - if (command) { - const newCommand = { - inputTimestamp: new Date().toISOString(), - ...command, - status: "formatted", - formatted: msg.data, - } satisfies ReplCommand; - Object.assign(command, newCommand); - this.#state.commands = [...this.#state.commands]; - this.#update(); - } - } - - if (msg.type === "result") { - const command = this.#state.commands.find( - (command) => command.key === msg.id, - ); - if (command) { - const newCommand = { - outputTimestamp: new Date().toISOString(), - ...command, - status: "success", - result: msg.data, - }; - Object.assign(command, newCommand); - this.#state.commands = [...this.#state.commands]; - this.#update(); - } - } - - if (msg.type === "log") { - const command = this.#state.commands.find( - (command) => command.key === msg.id, - ); - if (command) { - const newCommand = { - ...command, - logs: [...command.logs, msg.data], - }; - Object.assign(command, newCommand); - this.#state.commands = [...this.#state.commands]; - this.#update(); - } - } - - if (msg.type === "error") { - if (!msg.id) { - this.#state.status = { type: "error", error: msg.data }; - console.error("Actor Worker Error", msg.data); - this.#update(); - return; - } - - const command = this.#state.commands.find( - (command) => command.key === msg.id, - ); - if (command) { - const newCommand = { - outputTimestamp: new Date().toISOString(), - ...command, - status: "error", - error: msg.data, - }; - Object.assign(command, newCommand); - this.#state.commands = [...this.#state.commands]; - this.#update(); - } - } - - if (msg.type === "ready") { - if (this.#opts?.notifyOnReconnect) { - toast.success("Connected to Actor", { - id: "ac-ws-reconnect", - }); - } - this.#state.status = { type: "ready" }; - } - - if (msg.type === "inspect" || msg.type === "ready") { - this.#state.rpcs = [...msg.data.rpcs]; - this.#state.state = { - ...msg.data.state, - value: msg.data.state.value || {}, - }; - this.#state.connections = [...msg.data.connections]; - this.#update(); - } - - if (msg.type === "lost-connection") { - this.#state.status = { type: "pending" }; - - if (this.#opts?.notifyOnReconnect) { - toast.loading("Reconnecting...", { id: "ac-ws-reconnect" }); - } - this.#update(); - } - } - - #update() { - for (const listener of this.#listeners) { - listener(); - } - } -} diff --git a/frontend/packages/components/src/actors/worker/actor-worker-context.tsx b/frontend/packages/components/src/actors/worker/actor-worker-context.tsx deleted file mode 100644 index 0d731caae6..0000000000 --- a/frontend/packages/components/src/actors/worker/actor-worker-context.tsx +++ /dev/null @@ -1,158 +0,0 @@ -import { useAtomValue } from "jotai"; -import { selectAtom } from "jotai/utils"; -import { - type ReactNode, - createContext, - useCallback, - useContext, - useEffect, - useState, - useSyncExternalStore, -} from "react"; -import { toast } from "sonner"; -import { assertNonNullable } from "../../lib/utils"; -import { type Actor, type ActorAtom, ActorFeature } from "../actor-context"; -import { ActorWorkerContainer } from "./actor-worker-container"; - -export const ActorWorkerContext = createContext( - null, -); - -export const useActorWorker = () => { - const value = useContext(ActorWorkerContext); - assertNonNullable(value); - return value; -}; - -const selector = (a: Actor) => ({ - actorId: a.id, - endpoint: a.endpoint, - enabled: - !a.destroyedAt && - a.endpoint !== null && - a.startedAt !== null && - a.features?.includes(ActorFeature.Console), -}); - -interface ActorWorkerContextProviderProps { - actor: ActorAtom; - children: ReactNode; - notifyOnReconnect?: boolean; -} - -// FIXME: rewrite with jotai -export const ActorWorkerContextProvider = ({ - children, - actor, - notifyOnReconnect, -}: ActorWorkerContextProviderProps) => { - const { actorId, endpoint, enabled } = useAtomValue( - selectAtom(actor, selector), - ); - - const [container] = useState( - () => new ActorWorkerContainer(), - ); - - // biome-ignore lint/correctness/useExhaustiveDependencies: we want to create worker on each of those props change - useEffect(() => { - const ctrl = new AbortController(); - - if (enabled && endpoint) { - container.init({ - actorId, - endpoint, - notifyOnReconnect, - signal: ctrl.signal, - }); - } else { - toast.dismiss("ac-ws-reconnect"); - } - - return () => { - ctrl.abort(); - container.terminate(); - }; - }, [actorId, endpoint, enabled]); - - return ( - - {children} - - ); -}; - -export function useActorReplCommands() { - const container = useActorWorker(); - return useSyncExternalStore( - useCallback( - (cb) => { - return container.subscribe(cb); - }, - [container], - ), - useCallback(() => { - return container.getCommands(); - }, [container]), - ); -} - -export function useActorWorkerStatus() { - const container = useActorWorker(); - return useSyncExternalStore( - useCallback( - (cb) => { - return container.subscribe(cb); - }, - [container], - ), - useCallback(() => { - return container.getStatus(); - }, [container]), - ); -} - -export function useActorRpcs() { - const container = useActorWorker(); - return useSyncExternalStore( - useCallback( - (cb) => { - return container.subscribe(cb); - }, - [container], - ), - useCallback(() => { - return container.getRpcs(); - }, [container]), - ); -} - -export function useActorState() { - const container = useActorWorker(); - return useSyncExternalStore( - useCallback( - (cb) => { - return container.subscribe(cb); - }, - [container], - ), - useCallback(() => { - return container.getState(); - }, [container]), - ); -} - -export function useActorConnections() { - const container = useActorWorker(); - return useSyncExternalStore( - useCallback( - (cb) => { - return container.subscribe(cb); - }, - [container], - ), - useCallback(() => { - return container.getConnections(); - }, [container]), - ); -} diff --git a/frontend/packages/components/src/actors/worker/actor-worker-schema.ts b/frontend/packages/components/src/actors/worker/actor-worker-schema.ts deleted file mode 100644 index 755bb57b59..0000000000 --- a/frontend/packages/components/src/actors/worker/actor-worker-schema.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { InspectDataSchema } from "actor-core/inspector/protocol/actor"; -import { z } from "zod"; - -export type ReplErrorCode = - | "unsupported" - | "runtime_error" - | "timeout" - | "syntax"; - -const CodeMessageSchema = z.object({ - type: z.literal("code"), - data: z.string(), - id: z.string(), -}); -const InitMessageSchema = z.object({ - type: z.literal("init"), - endpoint: z.string(), - actorId: z.string(), - token: z.string().optional(), -}); - -const SetStateMessageSchema = z.object({ - type: z.literal("set-state"), - data: z.string(), -}); - -export const MessageSchema = z.discriminatedUnion("type", [ - CodeMessageSchema, - InitMessageSchema, - SetStateMessageSchema, -]); - -export const FormattedCodeSchema = z - .object({ - fg: z.string().optional(), - tokens: z.array( - z.array( - z.object({ - content: z.string(), - color: z.string().optional(), - }), - ), - ), - }) - .catch((ctx) => ctx.input); - -export const LogSchema = z.object({ - method: z.union([z.literal("log"), z.literal("warn"), z.literal("error")]), - data: z.array(z.any()).optional(), - timestamp: z.string().optional(), -}); - -export const ResponseSchema = z.discriminatedUnion("type", [ - z.object({ - type: z.literal("error"), - id: z.string().optional(), - data: z.any(), - }), - z.object({ - type: z.literal("formatted"), - id: z.string(), - data: FormattedCodeSchema, - }), - z.object({ - type: z.literal("result"), - id: z.string(), - data: z.any().optional(), - }), - z.object({ - type: z.literal("log"), - id: z.string(), - data: LogSchema, - }), - z.object({ - type: z.literal("ready"), - data: InspectDataSchema, - }), - z.object({ - type: z.literal("inspect"), - data: InspectDataSchema, - }), - z.object({ - type: z.literal("lost-connection"), - }), -]); - -export type Response = z.infer; -export type Message = z.infer; -export type FormattedCode = z.infer; -export type Log = z.infer; -export type InitMessage = z.infer; -export type CodeMessage = z.infer; -export type InspectData = z.infer; -export type SetStateMessage = z.infer; diff --git a/frontend/packages/components/src/actors/worker/actor-worker-status.tsx b/frontend/packages/components/src/actors/worker/actor-worker-status.tsx deleted file mode 100644 index 7838351392..0000000000 --- a/frontend/packages/components/src/actors/worker/actor-worker-status.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { Icon, faExclamationTriangle, faSpinner } from "@rivet-gg/icons"; -import { AnimatePresence, motion } from "framer-motion"; -import type { ContainerStatus } from "./actor-worker-container"; - -interface ActorWorkerStatusProps { - status: ContainerStatus["type"]; -} - -export function ActorWorkerStatus({ status }: ActorWorkerStatusProps) { - return ( - - {status === "pending" ? ( - - - Connecting to Actor... - - ) : null} - {status === "error" ? ( - - - Couldn't connect to Actor. - - ) : null} - {status === "unsupported" ? ( - - - Console is not supported for this Actor. - - ) : null} - - ); -} diff --git a/frontend/src/app.tsx b/frontend/src/app.tsx index 3934bb85a8..231cdd81f7 100644 --- a/frontend/src/app.tsx +++ b/frontend/src/app.tsx @@ -24,10 +24,19 @@ declare module "@tanstack/react-router" { } } +declare module "@tanstack/history" { + interface HistoryState { + // inspector specific + // indicates that the inspector was already connected as the result of submission of connection form + connectedInForm?: boolean; + } +} + declare module "@tanstack/react-query" { interface Register { queryMeta: { mightRequireAuth?: boolean; + statusCheck?: boolean; }; } } diff --git a/frontend/src/app/actor-builds-list.tsx b/frontend/src/app/actor-builds-list.tsx index 985b035733..22a66b2e94 100644 --- a/frontend/src/app/actor-builds-list.tsx +++ b/frontend/src/app/actor-builds-list.tsx @@ -24,7 +24,7 @@ export function ActorBuildsList() { ) : null} {data?.map((build) => ( diff --git a/frontend/src/app/actors.tsx b/frontend/src/app/actors.tsx index aae11f8c78..e67302a8dd 100644 --- a/frontend/src/app/actors.tsx +++ b/frontend/src/app/actors.tsx @@ -1,7 +1,6 @@ import { useQuery } from "@tanstack/react-query"; import { useNavigate, useSearch } from "@tanstack/react-router"; import { - ActorFeature, type ActorId, ActorNotFound, ActorsActorDetails, @@ -13,17 +12,7 @@ import { export function Actors({ actorId }: { actorId: string | undefined }) { return ( - {actorId ? ( - - ) : ( - - )} + {actorId ? : } ); } @@ -37,16 +26,7 @@ function Actor() { ); if (!data || isError) { - return ( - - ); + return ; } return ( diff --git a/frontend/src/app/build-prefiller.tsx b/frontend/src/app/build-prefiller.tsx index a1aae2f444..abfb643424 100644 --- a/frontend/src/app/build-prefiller.tsx +++ b/frontend/src/app/build-prefiller.tsx @@ -11,7 +11,7 @@ export function BuildPrefiller() { return ( ({ ...search, n: [data[0].name] })} + search={(search) => ({ ...search, n: [data[0].id] })} /> ); } diff --git a/frontend/src/app/connect.tsx b/frontend/src/app/connect.tsx index eaa0a68509..49576d0b13 100644 --- a/frontend/src/app/connect.tsx +++ b/frontend/src/app/connect.tsx @@ -29,7 +29,7 @@ export function Connect({

Get started with one of our quick start guides:

-
+

- Connect to your RivetKit project by entering the URL and - access token. + Connect to your Rivet Project by entering your RivetKit + URL.

diff --git a/frontend/src/app/credentials-context.tsx b/frontend/src/app/credentials-context.tsx deleted file mode 100644 index d40ea1dd47..0000000000 --- a/frontend/src/app/credentials-context.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { createContext, useContext } from "react"; - -export type InspectorCredentials = { - url: string; - token: string; -}; - -export const InspectorCredentialsContext = createContext<{ - credentials: InspectorCredentials | null; - setCredentials: (creds: InspectorCredentials | null) => void; -}>({ credentials: null, setCredentials: () => {} }); - -export const useInspectorCredentials = () => { - const ctx = useContext(InspectorCredentialsContext); - return ctx; -}; - -export const InspectorCredentialsProvider = - InspectorCredentialsContext.Provider; diff --git a/frontend/src/app/data-providers/default-data-provider.tsx b/frontend/src/app/data-providers/default-data-provider.tsx index d56dc63240..988846f5b3 100644 --- a/frontend/src/app/data-providers/default-data-provider.tsx +++ b/frontend/src/app/data-providers/default-data-provider.tsx @@ -1,25 +1,13 @@ +import type { Rivet } from "@rivetkit/engine-api-full"; import { infiniteQueryOptions, type MutationOptions, mutationOptions, type QueryKey, queryOptions, - UseInfiniteQueryOptions, } from "@tanstack/react-query"; -import type { - ActorId, - ActorLogEntry, - CreateActor as InspectorCreateActor, -} from "rivetkit/inspector"; import { z } from "zod"; -import { - type Actor, - type ActorMetrics, - type Build, - type CrashPolicy, - getActorStatus, - type Region, -} from "@/components/actors"; +import { type ActorId, getActorStatus } from "@/components/actors"; import { queryClient } from "@/queries/global"; export const ActorQueryOptionsSchema = z @@ -53,20 +41,7 @@ export type ActorQueryOptions = z.infer; export const RECORDS_PER_PAGE = 10; -type PaginatedResponse = { - pagination: { cursor?: string }; -} & Record; - -type PaginatedActorResponse = PaginatedResponse; -type PaginatedBuildsResponse = PaginatedResponse; -type PaginatedRegionsResponse = PaginatedResponse; - -type CreateActor = Omit & { - runnerNameSelector: string; - key: string; - crashPolicy: CrashPolicy; - datacenter?: string; -}; +type CreateActor = Omit; const defaultContext = { endpoint: "", @@ -82,6 +57,8 @@ const defaultContext = { refetchInterval: 2000, queryFn: async () => { throw new Error("Not implemented"); + // biome-ignore lint/correctness/noUnreachable: stub + return {} as Rivet.ActorsListResponse; }, getNextPageParam: (lastPage) => { if (lastPage.pagination.cursor) { @@ -96,7 +73,7 @@ const defaultContext = { return undefined; } - return lastPage.actors[lastPage.actors.length - 1].id; + return lastPage.actors[lastPage.actors.length - 1].actorId; }, }); }, @@ -109,12 +86,21 @@ const defaultContext = { refetchInterval: 2000, queryFn: async () => { throw new Error("Not implemented"); + // biome-ignore lint/correctness/noUnreachable: stub + return {} as Rivet.ActorsListNamesResponse; }, getNextPageParam: () => { return undefined; }, select: (data) => { - return data.pages.flatMap((page) => page.builds); + return data.pages.flatMap((page) => + Array.from( + Object.entries(page.names).map(([id, name]) => ({ + id, + name, + })), + ), + ); }, }); }, @@ -124,7 +110,7 @@ const defaultContext = { ...this.buildsQueryOptions(), select: (data) => { return data.pages.reduce((acc, page) => { - return acc + page.builds.length; + return acc + Object.keys(page.names).length; }, 0); }, }); @@ -137,7 +123,7 @@ const defaultContext = { refetchInterval: 5000, select: (data) => { return data.pages.flatMap((page) => - page.actors.map((actor) => actor.id), + page.actors.map((actor) => actor.actorId), ); }, }); @@ -148,7 +134,7 @@ const defaultContext = { ...this.actorsQueryOptions(opts), select: (data) => { return data.pages.flatMap((page) => - page.actors.map((actor) => actor.id), + page.actors.map((actor) => actor.actorId), ).length; }, }); @@ -158,24 +144,17 @@ const defaultContext = { actorQueryOptions(actorId: ActorId) { return queryOptions({ queryFn: async () => { - return {} as Actor; + return {} as Rivet.Actor; }, queryKey: ["actor", actorId] as QueryKey, }); }, - actorRegionQueryOptions(actorId: ActorId) { - return queryOptions({ - ...this.actorQueryOptions(actorId), - select: (data) => data.region, - }); - }, - actorDestroyedAtQueryOptions(actorId: ActorId) { return queryOptions({ ...this.actorQueryOptions(actorId), select: (data) => - data.destroyedAt ? new Date(data.destroyedAt) : null, + data.destroyTs ? new Date(data.destroyTs) : null, }); }, @@ -189,38 +168,28 @@ const defaultContext = { actorStatusAdditionalInfoQueryOptions(actorId: ActorId) { return queryOptions({ ...this.actorQueryOptions(actorId), - select: ({ rescheduleAt }) => ({ - rescheduleAt, + select: ({ rescheduleTs }) => ({ + rescheduleTs, }), }); }, - actorFeaturesQueryOptions(actorId: ActorId) { - return queryOptions({ - ...this.actorQueryOptions(actorId), - select: (data) => data.features ?? [], - }); - }, - actorGeneralQueryOptions(actorId: ActorId) { return queryOptions({ ...this.actorQueryOptions(actorId), select: (data) => ({ - tags: data.tags, keys: data.key, - createdAt: data.createdAt ? new Date(data.createdAt) : null, - destroyedAt: data.destroyedAt - ? new Date(data.destroyedAt) - : null, - connectableAt: data.connectableAt - ? new Date(data.connectableAt) + createTs: data.createTs ? new Date(data.createTs) : null, + destroyTs: data.destroyTs ? new Date(data.destroyTs) : null, + connectableTs: data.connectableTs + ? new Date(data.connectableTs) : null, - pendingAllocationAt: data.pendingAllocationAt - ? new Date(data.pendingAllocationAt) + pendingAllocationTs: data.pendingAllocationTs + ? new Date(data.pendingAllocationTs) : null, - sleepingAt: data.sleepingAt ? new Date(data.sleepingAt) : null, - region: data.region, - runner: data.runner, + sleepTs: data.sleepTs ? new Date(data.sleepTs) : null, + datacenter: data.datacenter, + runner: data.runnerNameSelector, crashPolicy: data.crashPolicy, }), }); @@ -234,15 +203,6 @@ const defaultContext = { enabled: false, }); }, - actorMetricsQueryOptions(actorId: ActorId) { - return queryOptions({ - queryKey: ["actor", actorId, "metrics"] as QueryKey, - queryFn: async () => { - throw new Error("Not implemented"); - }, - enabled: false, - }); - }, actorKeysQueryOptions(actorId: ActorId) { return queryOptions({ ...this.actorQueryOptions(actorId), @@ -279,77 +239,47 @@ const defaultContext = { initialPageParam: null as string | null, queryFn: async () => { throw new Error("Not implemented"); + // biome-ignore lint/correctness/noUnreachable: stub + return []; }, getNextPageParam: () => null, }); }, - actorNetworkQueryOptions(actorId: ActorId) { - return queryOptions({ - ...this.actorQueryOptions(actorId), - select: (data) => data.network, - }); - }, - actorNetworkPortsQueryOptions(actorId: ActorId) { - return queryOptions({ - ...this.actorNetworkQueryOptions(actorId), - select: (data) => data.network?.ports, - }); - }, - actorRuntimeQueryOptions(actorId: ActorId) { - return queryOptions({ - ...this.actorQueryOptions(actorId), - select: ({ runtime, lifecycle, tags }) => ({ - runtime, - lifecycle, - tags, - }), - }); - }, actorWorkerQueryOptions(actorId: ActorId) { return queryOptions({ ...this.actorQueryOptions(actorId), select: (data) => ({ - features: data.features ?? [], name: data.name ?? null, endpoint: this.endpoint ?? null, - destroyedAt: data.destroyedAt - ? new Date(data.destroyedAt) - : null, - runner: data.runner ?? undefined, - sleepingAt: data.sleepingAt ? new Date(data.sleepingAt) : null, - startedAt: data.startedAt ? new Date(data.startedAt) : null, + destroyedAt: data.destroyTs ? new Date(data.destroyTs) : null, + runner: data.runnerNameSelector ?? undefined, + sleepingAt: data.sleepTs ? new Date(data.sleepTs) : null, + startedAt: data.startTs ? new Date(data.startTs) : null, }), }); }, // #endregion - regionsQueryOptions() { + datacentersQueryOptions() { return infiniteQueryOptions({ queryKey: ["actor", "regions"] as QueryKey, initialPageParam: null as string | null, queryFn: async () => { throw new Error("Not implemented"); + // biome-ignore lint/correctness/noUnreachable: stub + return {} as Rivet.DatacentersListResponse; }, getNextPageParam: () => null, - select: (data) => data.pages.flatMap((page) => page.regions), + select: (data) => data.pages.flatMap((page) => page.datacenters), }); }, - regionQueryOptions(regionId: string | undefined) { + datacenterQueryOptions(regionId: string | undefined) { return queryOptions({ queryKey: ["actor", "region", regionId] as QueryKey, enabled: !!regionId, queryFn: async () => { throw new Error("Not implemented"); - }, - }); - }, - statusQueryOptions() { - return queryOptions({ - queryKey: ["status"] as QueryKey, - refetchInterval: 1000, - enabled: false, - retry: 0, - queryFn: async () => { - throw new Error("Not implemented"); + // biome-ignore lint/correctness/noUnreachable: stub + return {} as Rivet.Datacenter; }, }); }, @@ -358,6 +288,8 @@ const defaultContext = { mutationKey: ["createActor"] as QueryKey, mutationFn: async (_: CreateActor) => { throw new Error("Not implemented"); + // biome-ignore lint/correctness/noUnreachable: stub + return ""; }, onSuccess: () => { const keys = this.actorsQueryOptions({}).queryKey.filter( @@ -371,6 +303,35 @@ const defaultContext = { }, }); }, + + actorInspectorTokenQueryOptions(actorId: ActorId) { + return queryOptions({ + staleTime: 1000, + gcTime: 1000, + queryKey: ["tokens", actorId, "inspector"] as QueryKey, + queryFn: async () => { + throw new Error("Not implemented"); + // biome-ignore lint/correctness/noUnreachable: stub + return "" as string | null; + }, + }); + }, + + statusQueryOptions() { + return queryOptions({ + queryKey: ["status"] as QueryKey, + queryFn: async () => { + throw new Error("Not implemented"); + // biome-ignore lint/correctness/noUnreachable: stub + return true as boolean; + }, + enabled: false, + refetchInterval: 5000, + meta: { + statusCheck: true, + }, + }); + }, }; export type DefaultDataProvider = typeof defaultContext; diff --git a/frontend/src/app/data-providers/engine-data-provider.tsx b/frontend/src/app/data-providers/engine-data-provider.tsx index 3a651e6fc3..72da20b007 100644 --- a/frontend/src/app/data-providers/engine-data-provider.tsx +++ b/frontend/src/app/data-providers/engine-data-provider.tsx @@ -5,16 +5,10 @@ import { mutationOptions, type QueryKey, queryOptions, - UseQueryOptions, } from "@tanstack/react-query"; import z from "zod"; import { getConfig, ls } from "@/components"; -import { - type Actor, - ActorFeature, - type ActorId, - type CrashPolicy, -} from "@/components/actors"; +import type { ActorId } from "@/components/actors"; import { engineEnv } from "@/lib/env"; import { convertStringToId } from "@/lib/utils"; import { noThrow, shouldRetryAllExpect403 } from "@/queries/utils"; @@ -141,39 +135,17 @@ export const createNamespaceContext = ({ canCreateActors: true, canDeleteActors: true, }, - statusQueryOptions() { - return queryOptions({ - ...def.statusQueryOptions(), - queryKey: [{ namespace }, ...def.statusQueryOptions().queryKey], - enabled: true, - queryFn: async () => { - return true; - }, - retry: shouldRetryAllExpect403, - throwOnError: noThrow, - meta: { - mightRequireAuth, - }, - }); - }, - regionsQueryOptions() { + datacentersQueryOptions() { return infiniteQueryOptions({ - ...def.regionsQueryOptions(), + ...def.datacentersQueryOptions(), enabled: true, queryKey: [ { namespace }, - ...def.regionsQueryOptions().queryKey, + ...def.datacentersQueryOptions().queryKey, ] as QueryKey, queryFn: async () => { const data = await client.datacenters.list(); - return { - regions: data.datacenters.map((dc) => ({ - id: dc.name, - name: dc.name, - url: dc.url, - })), - pagination: data.pagination, - }; + return data; }, retry: shouldRetryAllExpect403, throwOnError: noThrow, @@ -182,27 +154,27 @@ export const createNamespaceContext = ({ }, }); }, - regionQueryOptions(regionId: string | undefined) { + datacenterQueryOptions(name: string | undefined) { return queryOptions({ - ...def.regionQueryOptions(regionId), + ...def.datacenterQueryOptions(name), queryKey: [ { namespace }, - ...def.regionQueryOptions(regionId).queryKey, + ...def.datacenterQueryOptions(name).queryKey, ], queryFn: async ({ client }) => { const regions = await client.ensureInfiniteQueryData( - this.regionsQueryOptions(), + this.datacentersQueryOptions(), ); for (const page of regions.pages) { - for (const region of page.regions) { - if (region.id === regionId) { + for (const region of page.datacenters) { + if (region.name === name) { return region; } } } - throw new Error(`Region not found: ${regionId}`); + throw new Error(`Region not found: ${name}`); }, retry: shouldRetryAllExpect403, throwOnError: noThrow, @@ -229,7 +201,7 @@ export const createNamespaceContext = ({ throw new Error("Actor not found"); } - return transformActor(data.actors[0]); + return data.actors[0]; }, retry: shouldRetryAllExpect403, throwOnError: noThrow, @@ -293,12 +265,7 @@ export const createNamespaceContext = ({ { abortSignal }, ); - return { - ...data, - actors: data.actors.map((actor) => - transformActor(actor), - ), - }; + return data; }, getNextPageParam: (lastPage) => { if (lastPage.actors.length < RECORDS_PER_PAGE) { @@ -328,22 +295,9 @@ export const createNamespaceContext = ({ { abortSignal }, ); - return { - pagination: data.pagination, - builds: Object.keys(data.names) - .sort() - .map((build) => ({ - id: build, - name: build, - })), - }; - }, - getNextPageParam: (lastPage) => { - if (lastPage.builds.length < RECORDS_PER_PAGE) { - return undefined; - } - return lastPage.pagination.cursor; + return data; }, + getNextPageParam: (lastPage) => lastPage.pagination?.cursor, retry: shouldRetryAllExpect403, throwOnError: noThrow, meta: { @@ -414,6 +368,32 @@ export const createNamespaceContext = ({ }, }); }, + actorInspectorTokenQueryOptions(actorId: ActorId) { + return queryOptions({ + queryKey: [ + { namespace }, + "actors", + actorId, + "inspector-token", + ] as QueryKey, + enabled: !!actorId, + queryFn: async ({ signal: abortSignal }) => { + // todo: check metadata if we're running serverless or not + const response = await client.actorsKvGet( + actorId, + // NOTE: MUST match key used in engine for actor inspector tokens + Buffer.from(Uint8Array.from([3])).toString("base64"), + { abortSignal }, + ); + + if (!response.value) { + return null; + } + + return response.value; + }, + }); + }, }; return { @@ -674,47 +654,6 @@ export const createNamespaceContext = ({ }; }; -function transformActor(a: Rivet.Actor): Actor { - return { - id: a.actorId as ActorId, - name: a.name, - key: a.key ? a.key : undefined, - connectableAt: a.connectableTs - ? new Date(a.connectableTs).toISOString() - : undefined, - region: a.datacenter, - createdAt: new Date(a.createTs).toISOString(), - startedAt: a.startTs ? new Date(a.startTs).toISOString() : undefined, - destroyedAt: a.destroyTs - ? new Date(a.destroyTs).toISOString() - : undefined, - sleepingAt: a.sleepTs ? new Date(a.sleepTs).toISOString() : undefined, - pendingAllocationAt: a.pendingAllocationTs - ? new Date(a.pendingAllocationTs).toISOString() - : undefined, - crashPolicy: a.crashPolicy as CrashPolicy, - runner: a.runnerNameSelector, - rescheduleAt: a.rescheduleTs - ? new Date(a.rescheduleTs).toISOString() - : undefined, - features: [ - ActorFeature.Config, - ActorFeature.Connections, - ActorFeature.State, - ActorFeature.Console, - ActorFeature.Database, - ActorFeature.EventsMonitoring, - ], - }; -} - -type RunnerConfig = [ - string, - { - datacenters: Record; - }, -]; - export function hasMetadataProvider( metadata: unknown, ): metadata is { provider?: string } { diff --git a/frontend/src/app/data-providers/inspector-data-provider.tsx b/frontend/src/app/data-providers/inspector-data-provider.tsx index be6afd2d9b..3d3cf9df80 100644 --- a/frontend/src/app/data-providers/inspector-data-provider.tsx +++ b/frontend/src/app/data-providers/inspector-data-provider.tsx @@ -1,20 +1,18 @@ -import { infiniteQueryOptions } from "@tanstack/react-query"; -import { - createManagerInspectorClient, - type Actor as InspectorActor, -} from "rivetkit/inspector"; -import type { Actor, ActorId } from "@/components/actors"; -import { ensureTrailingSlash } from "@/lib/utils"; - +import { queryOptions } from "@tanstack/react-query"; import { createDefaultGlobalContext, type DefaultDataProvider, } from "./default-data-provider"; +import { + createClient, + createGlobalContext as createGlobalEngineContext, + createNamespaceContext as createNamespaceEngineContext, +} from "./engine-data-provider"; export const createGlobalContext = (opts: { url?: string; token?: string }) => { const def = createDefaultGlobalContext(); - if (!opts.url || !opts.token) { + if (!opts.url) { return { ...def, endpoint: opts.url, @@ -24,164 +22,39 @@ export const createGlobalContext = (opts: { url?: string; token?: string }) => { }, }; } - - const client = createClient({ url: opts.url, token: opts.token }); - return { + const client = createClient(opts.url, { token: opts.token || "" }); + const global = { ...def, endpoint: opts.url, features: { canCreateActors: true, canDeleteActors: false, }, + ...createGlobalEngineContext({ + engineToken: () => opts.token!, + }), + }; + + return { + ...global, + ...createNamespaceEngineContext({ + ...global, + namespace: "default", + engineToken: () => opts.token!, + client, + }), statusQueryOptions() { - return { - ...def.statusQueryOptions(), - enabled: true, - queryFn: async ({ signal }) => { - const status = await client.ping.$get({ signal }); - if (!status.ok) { - throw new Error("Failed to fetch manager status"); - } - return true; - }, - }; - }, - regionsQueryOptions() { - return infiniteQueryOptions({ - ...def.regionsQueryOptions(), - enabled: true, + return queryOptions({ + ...global.statusQueryOptions(), queryFn: async () => { - return { - regions: [{ id: "default", name: "Default" }], - pagination: {}, - }; - }, - }); - }, - actorQueryOptions(actorId) { - return { - ...def.actorQueryOptions(actorId), - enabled: true, - queryFn: async ({ signal }) => { - const response = await client.actor[":id"].$get({ - param: { id: actorId }, - // @ts-expect-error - signal, - }); + const response = await fetch(opts.url || ""); if (!response.ok) { - throw response; - } - return transformActor(await response.json()); - }, - }; - }, - actorsQueryOptions(opts) { - return infiniteQueryOptions({ - ...def.actorsQueryOptions(opts), - enabled: true, - initialPageParam: undefined, - queryFn: async ({ signal, pageParam }) => { - const actors = await client.actors.$get({ - query: { cursor: pageParam, limit: 10 }, - signal, - }); - if (!actors.ok) { - throw new Error("Failed to fetch actors"); + throw new Error("Failed to fetch status"); } - const response = await actors.json(); - - return { - actors: response.map((actor) => transformActor(actor)), - pagination: { - cursor: - response.length === 10 - ? response[9].id - : undefined, - }, - }; - }, - }); - }, - buildsQueryOptions() { - return infiniteQueryOptions({ - ...def.buildsQueryOptions(), - enabled: true, - initialPageParam: undefined, - queryFn: async ({ signal, pageParam }) => { - const builds = await client.builds.$get({ - query: { cursor: pageParam, limit: 10 }, - signal, - }); - if (!builds.ok) { - throw new Error("Failed to fetch builds"); - } - const response = await builds.json(); - - return { - builds: response.map((build) => ({ - id: build.name, - name: build.name, - })), - pagination: { - cursor: - response.length === 10 - ? response[9].name - : undefined, - }, - }; + return true; }, + enabled: Boolean(opts.url), }); }, - createActorMutationOptions() { - return { - ...def.createActorMutationOptions(), - mutationFn: async (data) => { - const response = await client.actors.$post({ - json: { - key: [data.key], - name: data.name, - input: data.input, - }, - }); - if (!response.ok) { - throw new Error("Failed to create actor"); - } - const json = await response.json(); - if (!json?.id) { - throw new Error("Failed to create actor"); - } - return json.id; - }, - }; - }, } satisfies DefaultDataProvider; }; - -function transformActor(a: InspectorActor): Actor { - return { - id: a.id as ActorId, - name: a.name, - key: a.key.join(" "), - createdAt: a.createdAt - ? new Date(a.createdAt).toISOString() - : undefined, - destroyedAt: a.destroyedAt - ? new Date(a.destroyedAt).toISOString() - : undefined, - startedAt: a.createdAt - ? new Date(a.createdAt).toISOString() - : undefined, - features: a.features, - }; -} - -export function createClient({ url, token }: { url: string; token: string }) { - const newUrl = new URL(url); - if (!newUrl.pathname.endsWith("inspect")) { - newUrl.pathname = `${ensureTrailingSlash(newUrl.pathname)}inspect`; - } - - return createManagerInspectorClient(newUrl.href, { - headers: { Authorization: `Bearer ${token}` }, - }); -} diff --git a/frontend/src/app/dialogs/connect-manual-serverfull-frame.tsx b/frontend/src/app/dialogs/connect-manual-serverfull-frame.tsx index e788b4ba5f..d33c6b4248 100644 --- a/frontend/src/app/dialogs/connect-manual-serverfull-frame.tsx +++ b/frontend/src/app/dialogs/connect-manual-serverfull-frame.tsx @@ -58,11 +58,11 @@ export default function ConnectManualServerlfullFrameContent({ provider, }: ConnectManualServerlfullFrameContentProps) { usePrefetchInfiniteQuery({ - ...useEngineCompatDataProvider().regionsQueryOptions(), + ...useEngineCompatDataProvider().datacentersQueryOptions(), pages: Infinity, }); const { data } = useSuspenseInfiniteQuery( - useEngineCompatDataProvider().regionsQueryOptions(), + useEngineCompatDataProvider().datacentersQueryOptions(), ); const prefferedRegionForRailway = @@ -237,7 +237,9 @@ export const useSelectedDatacenter = () => { const datacenter = useWatch({ name: "datacenter" }); const { data } = useQuery( - useEngineCompatDataProvider().regionQueryOptions(datacenter || "auto"), + useEngineCompatDataProvider().datacenterQueryOptions( + datacenter || "auto", + ), ); return data?.url || engineEnv().VITE_APP_API_URL; diff --git a/frontend/src/app/dialogs/connect-manual-serverless-frame.tsx b/frontend/src/app/dialogs/connect-manual-serverless-frame.tsx index c94e1cfe63..4a18dad52e 100644 --- a/frontend/src/app/dialogs/connect-manual-serverless-frame.tsx +++ b/frontend/src/app/dialogs/connect-manual-serverless-frame.tsx @@ -68,12 +68,12 @@ export default function ConnectManualServerlessFrameContent({ onClose, }: ConnectManualServerlessFrameContentProps) { usePrefetchInfiniteQuery({ - ...useEngineCompatDataProvider().regionsQueryOptions(), + ...useEngineCompatDataProvider().datacentersQueryOptions(), pages: Infinity, }); const { data: datacenters } = useSuspenseInfiniteQuery( - useEngineCompatDataProvider().regionsQueryOptions(), + useEngineCompatDataProvider().datacentersQueryOptions(), ); return ; diff --git a/frontend/src/app/dialogs/connect-quick-railway-frame.tsx b/frontend/src/app/dialogs/connect-quick-railway-frame.tsx index 5a3bf6631c..1c8561ebf0 100644 --- a/frontend/src/app/dialogs/connect-quick-railway-frame.tsx +++ b/frontend/src/app/dialogs/connect-quick-railway-frame.tsx @@ -52,11 +52,11 @@ export default function ConnectQuickRailwayFrameContent({ onClose, }: ConnectQuickRailwayFrameContentProps) { usePrefetchInfiniteQuery({ - ...useEngineCompatDataProvider().regionsQueryOptions(), + ...useEngineCompatDataProvider().datacentersQueryOptions(), pages: Infinity, }); const { data } = useSuspenseInfiniteQuery( - useEngineCompatDataProvider().regionsQueryOptions(), + useEngineCompatDataProvider().datacentersQueryOptions(), ); const prefferedRegionForRailway = diff --git a/frontend/src/app/dialogs/connect-quick-vercel-frame.tsx b/frontend/src/app/dialogs/connect-quick-vercel-frame.tsx index 7d27a9b877..2717230ed3 100644 --- a/frontend/src/app/dialogs/connect-quick-vercel-frame.tsx +++ b/frontend/src/app/dialogs/connect-quick-vercel-frame.tsx @@ -31,12 +31,12 @@ export default function ConnectQuickVercelFrameContent({ onClose, }: ConnectQuickVercelFrameContentProps) { usePrefetchInfiniteQuery({ - ...useEngineCompatDataProvider().regionsQueryOptions(), + ...useEngineCompatDataProvider().datacentersQueryOptions(), pages: Infinity, }); const { data: datacenters } = useSuspenseInfiniteQuery( - useEngineCompatDataProvider().regionsQueryOptions(), + useEngineCompatDataProvider().datacentersQueryOptions(), ); return ( diff --git a/frontend/src/app/dialogs/connect-railway-frame.tsx b/frontend/src/app/dialogs/connect-railway-frame.tsx index 583714263b..3db2845447 100644 --- a/frontend/src/app/dialogs/connect-railway-frame.tsx +++ b/frontend/src/app/dialogs/connect-railway-frame.tsx @@ -63,11 +63,11 @@ export default function ConnectRailwayFrameContent({ onClose, }: ConnectRailwayFrameContentProps) { usePrefetchInfiniteQuery({ - ...useEngineCompatDataProvider().regionsQueryOptions(), + ...useEngineCompatDataProvider().datacentersQueryOptions(), pages: Infinity, }); const { data } = useSuspenseInfiniteQuery( - useEngineCompatDataProvider().regionsQueryOptions(), + useEngineCompatDataProvider().datacentersQueryOptions(), ); const prefferedRegionForRailway = diff --git a/frontend/src/app/dialogs/connect-vercel-frame.tsx b/frontend/src/app/dialogs/connect-vercel-frame.tsx index 0f06a0f75e..6afa8b0f0d 100644 --- a/frontend/src/app/dialogs/connect-vercel-frame.tsx +++ b/frontend/src/app/dialogs/connect-vercel-frame.tsx @@ -64,12 +64,12 @@ export default function CreateProjectFrameContent({ onClose, }: CreateProjectFrameContentProps) { usePrefetchInfiniteQuery({ - ...useEngineCompatDataProvider().regionsQueryOptions(), + ...useEngineCompatDataProvider().datacentersQueryOptions(), pages: Infinity, }); const { data: datacenters } = useSuspenseInfiniteQuery( - useEngineCompatDataProvider().regionsQueryOptions(), + useEngineCompatDataProvider().datacentersQueryOptions(), ); return ( diff --git a/frontend/src/app/forms/connect-manual-serverless-form.tsx b/frontend/src/app/forms/connect-manual-serverless-form.tsx index 13ff1f6897..034fcb7283 100644 --- a/frontend/src/app/forms/connect-manual-serverless-form.tsx +++ b/frontend/src/app/forms/connect-manual-serverless-form.tsx @@ -105,7 +105,7 @@ export const RunnerName = function RunnerName() { export const Datacenters = function Datacenter() { const { control } = useFormContext(); const { data, hasNextPage, fetchNextPage } = useInfiniteQuery( - useEngineCompatDataProvider().regionsQueryOptions(), + useEngineCompatDataProvider().datacentersQueryOptions(), ); return ( diff --git a/frontend/src/app/inspector-root.tsx b/frontend/src/app/inspector-root.tsx index 7ed38741e3..fbde7328aa 100644 --- a/frontend/src/app/inspector-root.tsx +++ b/frontend/src/app/inspector-root.tsx @@ -1,21 +1,20 @@ import { CatchBoundary, + useLocation, useNavigate, useRouteContext, useSearch, } from "@tanstack/react-router"; -import { useEffect, useMemo, useRef, useState } from "react"; +import { useRef } from "react"; import { match } from "ts-pattern"; import { Actors } from "./actors"; import { BuildPrefiller } from "./build-prefiller"; import { Connect } from "./connect"; -import { InspectorCredentialsProvider } from "./credentials-context"; -import { createClient } from "./data-providers/inspector-data-provider"; import { Logo } from "./logo"; import { RouteLayout } from "./route-layout"; export function InspectorRoot() { - const alreadyConnected = useRouteContext({ + const connectedInPreflight = useRouteContext({ from: "/_context/", select: (ctx) => match(ctx) @@ -26,40 +25,31 @@ export function InspectorRoot() { ) .otherwise(() => null), }); + const connectedInForm = useLocation({ + select: (loc) => + "connectedInForm" in loc.state + ? (loc.state.connectedInForm ?? false) + : false, + }); + + const alreadyConnected = connectedInPreflight || connectedInForm; + const navigate = useNavigate(); const search = useSearch({ from: "/_context" }); - const [credentials, setCredentials] = useState(alreadyConnected ? { url: search.u!, token: search.t! } : null); const formRef = useRef(null); - useEffect(() => { - if (search.t) { - formRef.current?.requestSubmit(); - } - }, [search.t]); - - const ctxValue = useMemo(() => { - return { credentials, setCredentials }; - }, [credentials]); - - if (credentials || alreadyConnected) { + if (alreadyConnected) { return ( - - - - - search.n?.join(",") ?? "no-build-name" - } - errorComponent={() => null} - > - {!search.n ? : null} - - - + + + search.n?.join(",") ?? "no-build-name"} + errorComponent={() => null} + > + {!search.n ? : null} + + ); } @@ -71,32 +61,29 @@ export function InspectorRoot() { formRef={formRef} onSubmit={async (values, form) => { try { - const client = createClient({ - url: values.username, - token: values.token, + const response = await fetch(values.url, { + method: "OPTIONS", }); - const resp = await client.ping.$get(); - if (!resp.ok) { - throw resp; + if (!response.ok) { + throw new Error("CORS preflight failed"); } await navigate({ to: "/", search: (old) => { return { ...old, - u: values.username, - t: values.token, + u: values.url, }; }, - }); - setCredentials({ - url: values.username, - token: values.token, + state: (old) => ({ + ...old, + connectedInForm: true, + }), }); } catch { - form.setError("token", { + form.setError("url", { message: - "Failed to connect. Please check your URL and token.", + "Failed to connect. Please check your URL, and CORS settings.", }); } }} diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 6c586171f3..e8863042b5 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -1,11 +1,10 @@ -import { useClerk } from "@clerk/clerk-react"; import { faArrowUpRight, faBolt, faHome, faKey, faLink, - faServer, + faLinkSlash, faSpinnerThird, Icon, } from "@rivet-gg/icons"; @@ -42,19 +41,14 @@ import { ResizablePanelGroup, ScrollArea, Skeleton, + WithTooltip, } from "@/components"; -import { - useDataProvider, - useDataProviderCheck, - useInspectorDataProvider, -} from "@/components/actors"; +import { useDataProvider, useDataProviderCheck } from "@/components/actors"; import type { HeaderLinkProps } from "@/components/header/header-link"; import { ensureTrailingSlash } from "@/lib/utils"; import { ActorBuildsList } from "./actor-builds-list"; import { Changelog } from "./changelog"; import { ContextSwitcher } from "./context-switcher"; -import { useInspectorCredentials } from "./credentials-context"; -import { HelpDropdown } from "./help-dropdown"; import { NamespaceSelect } from "./namespace-select"; import { UserDropdown } from "./user-dropdown"; @@ -415,14 +409,12 @@ const Subnav = () => { Overview ) : null} - {hasDataProvider && hasQuery ? ( -
- - Actors - - -
- ) : null} +
+ + Instances + + +
); }; @@ -470,11 +462,11 @@ function ConnectionStatus(): ReactNode { from: "/_context", select: (s) => s.u, }); - const data = useInspectorDataProvider(); - const { setCredentials } = useInspectorCredentials(); + const data = useDataProvider(); const { isLoading, isError, isSuccess } = useQuery( data.statusQueryOptions(), ); + const navigate = useNavigate(); if (isLoading) { return ( @@ -504,7 +496,7 @@ function ConnectionStatus(): ReactNode { variant="outline" size="xs" className="ml-2 text-foreground" - onClick={() => setCredentials(null)} + onClick={() => navigate({ to: ".", state: {} })} startIcon={} > Reconnect @@ -515,11 +507,26 @@ function ConnectionStatus(): ReactNode { if (isSuccess) { return ( -
+

Connected

{endpoint}

+ + navigate({ to: ".", state: {} })} + > + + + } + content="Disconnect" + />
); } diff --git a/frontend/src/components/actors/actor-build.tsx b/frontend/src/components/actors/actor-build.tsx deleted file mode 100644 index c19adb9ae1..0000000000 --- a/frontend/src/components/actors/actor-build.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { useQuery } from "@tanstack/react-query"; -import { Dd, DiscreteCopyButton, Dl, Dt, Flex } from "@/components"; -import { useDataProvider } from "./data-provider"; -import type { ActorId } from "./queries"; - -interface ActorBuildProps { - actorId: ActorId; -} - -export function ActorBuild({ actorId }: ActorBuildProps) { - const { data } = useQuery( - useDataProvider().actorBuildQueryOptions(actorId), - ); - - if (!data) { - return null; - } - - return ( -
-
-

Build

-
- -
-
ID
-
- - {data.id} - -
-
-
-
- ); -} diff --git a/frontend/src/components/actors/actor-clear-events-log-button.tsx b/frontend/src/components/actors/actor-clear-events-log-button.tsx index 3c35506e2a..37283911ef 100644 --- a/frontend/src/components/actors/actor-clear-events-log-button.tsx +++ b/frontend/src/components/actors/actor-clear-events-log-button.tsx @@ -1,26 +1,26 @@ import { faBroomWide, Icon } from "@rivet-gg/icons"; import { Button } from "../ui/button"; import { WithTooltip } from "../ui/tooltip"; -import { type ActorId, useActorClearEventsMutation } from "./queries"; +import type { ActorId } from "./queries"; export function ActorClearEventsLogButton({ actorId }: { actorId: ActorId }) { - const { mutate, isPending } = useActorClearEventsMutation(actorId); - - return ( - { - mutate(); - }} - > - - - } - /> - ); + // const { mutate, isPending } = useActorClearEventsMutation(actorId); + return null; + // return ( + // { + // mutate(); + // }} + // > + // + // + // } + // /> + // ); } diff --git a/frontend/src/components/actors/actor-config-tab.tsx b/frontend/src/components/actors/actor-config-tab.tsx index 19b9b5841c..9e9342e099 100644 --- a/frontend/src/components/actors/actor-config-tab.tsx +++ b/frontend/src/components/actors/actor-config-tab.tsx @@ -1,8 +1,6 @@ import { faBooks, Icon } from "@rivet-gg/icons"; import { Button, DocsSheet, ScrollArea } from "@/components"; import { ActorGeneral } from "./actor-general"; -import { ActorNetwork } from "./actor-network"; -import { ActorRuntime } from "./actor-runtime"; import type { ActorId } from "./queries"; interface ActorConfigTabProps { @@ -24,8 +22,6 @@ export function ActorConfigTab(props: ActorConfigTabProps) {
- - ); } diff --git a/frontend/src/components/actors/actor-connections-tab.tsx b/frontend/src/components/actors/actor-connections-tab.tsx index 36b04f402d..325da20259 100644 --- a/frontend/src/components/actors/actor-connections-tab.tsx +++ b/frontend/src/components/actors/actor-connections-tab.tsx @@ -1,9 +1,9 @@ import { useQuery } from "@tanstack/react-query"; import { LiveBadge, ScrollArea } from "@/components"; -import { useActor } from "./actor-queries-context"; import { ActorObjectInspector } from "./console/actor-inspector"; import { useDataProvider } from "./data-provider"; -import { type ActorId, useActorConnectionsStream } from "./queries"; +import { useActorInspector } from "./inspector-context"; +import type { ActorId } from "./queries"; interface ActorConnectionsTabProps { actorId: ActorId; @@ -14,14 +14,10 @@ export function ActorConnectionsTab({ actorId }: ActorConnectionsTabProps) { useDataProvider().actorDestroyedAtQueryOptions(actorId), ); - const actorQueries = useActor(); - const { - data: { connections } = {}, - isError, - isLoading, - } = useQuery(actorQueries.actorConnectionsQueryOptions(actorId)); - - // useActorConnectionsStream(actorId); + const inspector = useActorInspector(); + const { data = [] } = useQuery( + inspector.actorConnectionsQueryOptions(actorId), + ); if (destroyedAt) { return ( @@ -31,24 +27,6 @@ export function ActorConnectionsTab({ actorId }: ActorConnectionsTabProps) { ); } - if (isError) { - return ( -
- Connections Preview is currently unavailable. -
- See console/logs for more details. -
- ); - } - - if (isLoading) { - return ( -
- Loading connections... -
- ); - } - return (
@@ -57,7 +35,7 @@ export function ActorConnectionsTab({ actorId }: ActorConnectionsTabProps) {
diff --git a/frontend/src/components/actors/actor-context.tsx b/frontend/src/components/actors/actor-context.tsx deleted file mode 100644 index d62efbb389..0000000000 --- a/frontend/src/components/actors/actor-context.tsx +++ /dev/null @@ -1,4 +0,0 @@ -export { - createActorInspectorClient, - createManagerInspectorClient, -} from "rivetkit/inspector"; diff --git a/frontend/src/components/actors/actor-database.tsx b/frontend/src/components/actors/actor-database.tsx index fa65ed4f9a..918794f6e1 100644 --- a/frontend/src/components/actors/actor-database.tsx +++ b/frontend/src/components/actors/actor-database.tsx @@ -13,8 +13,8 @@ import { SelectValue, } from "../ui/select"; import { WithTooltip } from "../ui/tooltip"; -import { useActor } from "./actor-queries-context"; import { DatabaseTable } from "./database/database-table"; +import { useActorInspector } from "./inspector-context"; import type { ActorId } from "./queries"; interface ActorDatabaseProps { @@ -22,9 +22,9 @@ interface ActorDatabaseProps { } export function ActorDatabase({ actorId }: ActorDatabaseProps) { - const actorQueries = useActor(); + const actorInspector = useActorInspector(); const { data, refetch } = useQuery( - actorQueries.actorDatabaseQueryOptions(actorId), + actorInspector.actorDatabaseQueryOptions(actorId), ); const [table, setTable] = useState( () => data?.db?.[0]?.table.name, @@ -36,11 +36,13 @@ export function ActorDatabase({ actorId }: ActorDatabaseProps) { data: rows, refetch: refetchData, isLoading, - } = useQuery( - actorQueries.actorDatabaseRowsQueryOptions(actorId, selectedTable!, { - enabled: !!selectedTable, - }), - ); + } = useQuery({ + ...actorInspector.actorDatabaseRowsQueryOptions( + actorId, + selectedTable!, + ), + enabled: !!selectedTable, + }); const currentTable = data?.db?.find( (db) => db.table.name === selectedTable, @@ -126,7 +128,7 @@ function TableSelect({ onSelect: (table: string) => void; value: string | undefined; }) { - const actorQueries = useActor(); + const actorQueries = useActorInspector(); const { data: tables } = useQuery( actorQueries.actorDatabaseTablesQueryOptions(actorId), ); diff --git a/frontend/src/components/actors/actor-db-tab.tsx b/frontend/src/components/actors/actor-db-tab.tsx index f3fcc1097c..deccca9510 100644 --- a/frontend/src/components/actors/actor-db-tab.tsx +++ b/frontend/src/components/actors/actor-db-tab.tsx @@ -1,8 +1,8 @@ import { useQuery } from "@tanstack/react-query"; import { ActorDatabase } from "./actor-database"; -import { useActor } from "./actor-queries-context"; import { Info } from "./actor-state-tab"; import { useDataProvider } from "./data-provider"; +import { useActorInspector } from "./inspector-context"; import type { ActorId } from "./queries"; interface ActorDatabaseTabProps { @@ -14,12 +14,12 @@ export function ActorDatabaseTab({ actorId }: ActorDatabaseTabProps) { useDataProvider().actorDestroyedAtQueryOptions(actorId), ); - const actorQueries = useActor(); + const actorInspector = useActorInspector(); const { data: isEnabled, isLoading, isError, - } = useQuery(actorQueries.actorDatabaseEnabledQueryOptions(actorId)); + } = useQuery(actorInspector.actorDatabaseEnabledQueryOptions(actorId)); if (destroyedAt) { return Database Studio is unavailable for inactive Actors.; diff --git a/frontend/src/components/actors/actor-editable-state.tsx b/frontend/src/components/actors/actor-editable-state.tsx index f631915450..5bcc42cdbf 100644 --- a/frontend/src/components/actors/actor-editable-state.tsx +++ b/frontend/src/components/actors/actor-editable-state.tsx @@ -1,4 +1,5 @@ import { faRotateLeft, faSave, Icon } from "@rivet-gg/icons"; +import { useMutation, useQuery } from "@tanstack/react-query"; import { AnimatePresence, motion } from "framer-motion"; import { useMemo, useRef, useState } from "react"; import { @@ -14,7 +15,8 @@ import { JsonCode, } from "@/components/code-mirror"; import { ActorStateChangeIndicator } from "./actor-state-change-indicator"; -import { type ActorId, useActorStatePatchMutation } from "./queries"; +import { useActorInspector } from "./inspector-context"; +import type { ActorId } from "./queries"; const isValidJson = (json: string | null): json is string => { if (!json) return false; @@ -28,34 +30,31 @@ const isValidJson = (json: string | null): json is string => { interface ActorEditableStateProps { actorId: ActorId; - state: unknown; } -export function ActorEditableState({ - state, - actorId, -}: ActorEditableStateProps) { +export function ActorEditableState({ actorId }: ActorEditableStateProps) { + const actorInspector = useActorInspector(); + const { data: state } = useQuery( + actorInspector.actorStateQueryOptions(actorId), + ); + const [isEditing, setIsEditing] = useState(false); const [value, setValue] = useState(null); - const ref = useRef(null); - const formatted = useMemo(() => { return JSON.stringify(state, null, 2); }, [state]); - const isValid = isValidJson(value) ? JSON.parse(value) : false; - const { mutateAsync, isPending } = useActorStatePatchMutation(actorId); - - // useActorStateStream(actorId); + const { mutateAsync, isPending } = useMutation( + actorInspector.actorStatePatchMutation(actorId), + ); return ( <>
{isEditing ? : } -
diff --git a/frontend/src/components/actors/actor-events-list.tsx b/frontend/src/components/actors/actor-events-list.tsx index 69c00ef480..e73245cb38 100644 --- a/frontend/src/components/actors/actor-events-list.tsx +++ b/frontend/src/components/actors/actor-events-list.tsx @@ -9,10 +9,9 @@ import { import { useQuery } from "@tanstack/react-query"; import { format } from "date-fns"; import { type PropsWithChildren, useEffect, useRef } from "react"; -import type { RecordedRealtimeEvent } from "rivetkit/inspector"; import { Badge } from "../ui/badge"; -import { useActor } from "./actor-queries-context"; import { ActorObjectInspector } from "./console/actor-inspector"; +import { useActorInspector } from "./inspector-context"; import type { ActorId } from "./queries"; interface ActorEventsListProps { @@ -26,9 +25,9 @@ export function ActorEventsList({ search, filter, }: ActorEventsListProps) { - const actorQueries = useActor(); + const actorInspector = useActorInspector(); const { data, isLoading, isError } = useQuery( - actorQueries.actorEventsQueryOptions(actorId), + actorInspector.actorEventsQueryOptions(actorId), ); if (isLoading) { @@ -45,7 +44,7 @@ export function ActorEventsList({ ); } - const filteredEvents = data?.events.filter?.((event) => { + const filteredEvents = data?.filter?.((event) => { const constraints = []; if ("name" in event) { diff --git a/frontend/src/components/actors/actor-events-tab.tsx b/frontend/src/components/actors/actor-events-tab.tsx index 6deb04786f..93a0cf4d13 100644 --- a/frontend/src/components/actors/actor-events-tab.tsx +++ b/frontend/src/components/actors/actor-events-tab.tsx @@ -1,8 +1,8 @@ import { useQuery } from "@tanstack/react-query"; import { ActorEvents } from "./actor-events"; -import { useActor } from "./actor-queries-context"; import { Info } from "./actor-state-tab"; import { useDataProvider } from "./data-provider"; +import { useActorInspector } from "./inspector-context"; import type { ActorId } from "./queries"; export type EventsTypeFilter = "action" | "subscription" | "broadcast" | "send"; @@ -17,7 +17,7 @@ export function ActorEventsTab({ actorId }: ActorEventsTabProps) { ); const { isError, isLoading } = useQuery( - useActor().actorEventsQueryOptions(actorId), + useActorInspector().actorEventsQueryOptions(actorId), ); if (destroyedAt) { diff --git a/frontend/src/components/actors/actor-events.tsx b/frontend/src/components/actors/actor-events.tsx index f9060a0352..5c1cec7ead 100644 --- a/frontend/src/components/actors/actor-events.tsx +++ b/frontend/src/components/actors/actor-events.tsx @@ -22,8 +22,8 @@ import { ActorClearEventsLogButton } from "./actor-clear-events-log-button"; import { useActorDetailsSettings } from "./actor-details-settings"; import { ActorDetailsSettingsButton } from "./actor-details-settings-button"; import { ActorEventsList } from "./actor-events-list"; -import { useActor } from "./actor-queries-context"; -import { type ActorId, useActorEventsStream } from "./queries"; +import { useActorInspector } from "./inspector-context"; +import type { ActorId } from "./queries"; export type EventsTypeFilter = "action" | "subscription" | "broadcast" | "send"; @@ -42,10 +42,9 @@ export function ActorEvents({ actorId }: ActorEventsProps) { const [isLive, setIsLive] = useState(true); const ref = useRef(null); - // useActorEventsStream(actorId, { enabled: isLive }); const [settings] = useActorDetailsSettings(); - const actorQueries = useActor(); + const actorQueries = useActorInspector(); const { data } = useQuery(actorQueries.actorEventsQueryOptions(actorId)); const { onScroll } = useScrollToBottom(ref, [data]); diff --git a/frontend/src/components/actors/actor-filters-context.tsx b/frontend/src/components/actors/actor-filters-context.tsx index 92ed89dbef..4b181f6180 100644 --- a/frontend/src/components/actors/actor-filters-context.tsx +++ b/frontend/src/components/actors/actor-filters-context.tsx @@ -43,12 +43,16 @@ export const ACTORS_FILTERS_DEFINITIONS = { category: "display", ephemeral: true, }, - showDatacenter: { - type: "boolean", - label: "Show Actors Datacenter", - category: "display", - ephemeral: true, - }, + ...(__APP_TYPE__ === "engine" || __APP_TYPE__ === "cloud" + ? { + showDatacenter: { + type: "boolean", + label: "Show Actors Datacenter", + category: "display", + ephemeral: true, + }, + } + : {}), wakeOnSelect: { type: "boolean", label: "Auto-wake Actors on select", diff --git a/frontend/src/components/actors/actor-network.tsx b/frontend/src/components/actors/actor-network.tsx deleted file mode 100644 index 3e6fb60c79..0000000000 --- a/frontend/src/components/actors/actor-network.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import { faBooks, Icon } from "@rivet-gg/icons"; -import { useQuery } from "@tanstack/react-query"; -import { Fragment } from "react"; -import { - Button, - cn, - Dd, - DiscreteCopyButton, - Dl, - DocsSheet, - Dt, - Flex, -} from "@/components"; -import { ActorObjectInspector } from "./console/actor-inspector"; -import { useDataProvider } from "./data-provider"; -import type { ActorId } from "./queries"; - -export interface ActorNetworkProps { - actorId: ActorId; -} - -export function ActorNetwork({ actorId }: ActorNetworkProps) { - const { data: ports } = useQuery( - useDataProvider().actorNetworkPortsQueryOptions(actorId), - ); - if (!ports) { - return null; - } - - return ( -
-
-

Network

- - - -
-
- -
-
Ports
-
- {Object.entries(ports).map( - ([name, port], index) => ( - - - {name} - -
-
Protocol
-
- - {port.protocol} - -
-
Port
-
- - {port.port} - -
-
Hostname
-
- - - {port.hostname} - - -
- {port.url ? ( - <> -
URL
-
- - - {port.url} - - -
- - ) : null} - - {port.routing?.host ? ( - <> -
Host Routing
-
- - - -
- - ) : null} -
-
- ), - )} -
-
-
-
-
- ); -} diff --git a/frontend/src/components/actors/actor-not-found.tsx b/frontend/src/components/actors/actor-not-found.tsx index c5a28f1370..bba3f1b5f8 100644 --- a/frontend/src/components/actors/actor-not-found.tsx +++ b/frontend/src/components/actors/actor-not-found.tsx @@ -7,15 +7,9 @@ import { FilterOp } from "../ui/filters"; import { ActorTabs } from "./actors-actor-details"; import { useActorsView } from "./actors-view-context-provider"; import { useDataProvider } from "./data-provider"; -import type { ActorFeature, ActorId } from "./queries"; +import type { ActorId } from "./queries"; -export function ActorNotFound({ - actorId, - features = [], -}: { - features?: ActorFeature[]; - actorId?: ActorId; -}) { +export function ActorNotFound({ actorId }: { actorId?: ActorId }) { const { copy } = useActorsView(); const navigate = useNavigate(); @@ -30,7 +24,7 @@ export function ActorNotFound({ return (
- +
{!isLoading ? ( <> diff --git a/frontend/src/components/actors/actor-queries-context.tsx b/frontend/src/components/actors/actor-queries-context.tsx deleted file mode 100644 index c9ef62ab70..0000000000 --- a/frontend/src/components/actors/actor-queries-context.tsx +++ /dev/null @@ -1,270 +0,0 @@ -import { queryOptions } from "@tanstack/react-query"; -import { createContext, useContext } from "react"; -import { - createActorInspectorClient, - type RecordedRealtimeEvent, -} from "rivetkit/inspector"; -import type { ActorId } from "./queries"; - -type RequestOptions = Parameters[1]; - -export const createDefaultActorContext = ( - { hash }: { hash: string } = { hash: `${Date.now()}` }, -) => ({ - createActorInspectorFetchConfiguration: async ( - actorId: ActorId | string, - opts: { auth?: boolean } = { auth: true }, - ): Promise => ({ - headers: { - "X-RivetKit-Query": JSON.stringify({ - getForId: { actorId }, - }), - }, - }), - createActorInspectorUrl(actorId: ActorId | string) { - return "http://localhost:6420/registry/actors/inspect"; - }, - async createActorInspector( - actorId: ActorId | string, - opts: { auth?: boolean } = { auth: true }, - ) { - return createActorInspectorClient( - this.createActorInspectorUrl(actorId), - await this.createActorInspectorFetchConfiguration(actorId, opts), - ); - }, - actorPingQueryOptions( - actorId: ActorId, - opts: { enabled?: boolean; refetchInterval?: number | false } = {}, - ) { - return queryOptions({ - enabled: false, - refetchInterval: 1000, - ...opts, - queryKey: [hash, "actor", actorId, "ping"], - queryFn: async ({ queryKey: [, , actorId] }) => { - const client = await this.createActorInspector(actorId); - const response = await client.ping.$get(); - if (!response.ok) { - throw response; - } - return await response.json(); - }, - }); - }, - - actorStateQueryOptions( - actorId: ActorId, - { enabled }: { enabled: boolean } = { enabled: true }, - ) { - return queryOptions({ - enabled, - refetchInterval: 1000, - queryKey: [hash, "actor", actorId, "state"], - queryFn: async ({ queryKey: [, , actorId] }) => { - const client = await this.createActorInspector(actorId); - const response = await client.state.$get(); - - if (!response.ok) { - throw response; - } - return (await response.json()) as { - enabled: boolean; - state: unknown; - }; - }, - }); - }, - - actorConnectionsQueryOptions( - actorId: ActorId, - { enabled }: { enabled: boolean } = { enabled: true }, - ) { - return queryOptions({ - enabled, - refetchInterval: 1000, - queryKey: [hash, "actor", actorId, "connections"], - queryFn: async ({ queryKey: [, , actorId] }) => { - const client = await this.createActorInspector(actorId); - const response = await client.connections.$get(); - - if (!response.ok) { - throw response; - } - return await response.json(); - }, - }); - }, - - actorDatabaseQueryOptions( - actorId: ActorId, - { enabled }: { enabled: boolean } = { enabled: true }, - ) { - return queryOptions({ - enabled, - queryKey: [hash, "actor", actorId, "database"], - queryFn: async ({ queryKey: [, , actorId] }) => { - const client = await this.createActorInspector(actorId); - const response = await client.db.$get(); - - if (!response.ok) { - throw response; - } - return await response.json(); - }, - }); - }, - - actorDatabaseEnabledQueryOptions( - actorId: ActorId, - { enabled }: { enabled: boolean } = { enabled: true }, - ) { - return queryOptions({ - ...this.actorDatabaseQueryOptions(actorId, { enabled }), - select: (data) => data.enabled, - notifyOnChangeProps: ["data", "isError", "isLoading"], - }); - }, - - actorDatabaseTablesQueryOptions( - actorId: ActorId, - { enabled }: { enabled: boolean } = { enabled: true }, - ) { - return queryOptions({ - ...this.actorDatabaseQueryOptions(actorId, { enabled }), - select: (data) => - data.db?.map((table) => ({ - name: table.table.name, - type: table.table.type, - records: table.records, - })) || [], - notifyOnChangeProps: ["data", "isError", "isLoading"], - }); - }, - - actorDatabaseRowsQueryOptions( - actorId: ActorId, - table: string, - { enabled }: { enabled: boolean } = { enabled: true }, - ) { - return queryOptions({ - enabled, - staleTime: 0, - gcTime: 5000, - queryKey: [hash, "actor", actorId, "database", table], - queryFn: async ({ queryKey: [, , actorId, , table] }) => { - const client = await this.createActorInspector(actorId); - const response = await client.db.$post({ - json: { query: `SELECT * FROM ${table} LIMIT 500` }, - }); - if (!response.ok) { - throw response; - } - return await response.json(); - }, - }); - }, - - actorEventsQueryOptions( - actorId: ActorId, - { enabled }: { enabled: boolean } = { enabled: true }, - ) { - return queryOptions({ - enabled, - refetchInterval: 1000, - queryKey: [hash, "actor", actorId, "events"], - queryFn: async ({ queryKey: [, , actorId] }) => { - const client = await this.createActorInspector(actorId); - const response = await client.events.$get(); - - if (!response.ok) { - throw response; - } - return (await response.json()) as { - events: RecordedRealtimeEvent[]; - }; - }, - }); - }, - - actorRpcsQueryOptions( - actorId: ActorId, - { enabled }: { enabled: boolean } = { enabled: true }, - ) { - return queryOptions({ - enabled, - queryKey: [hash, "actor", actorId, "rpcs"], - queryFn: async ({ queryKey: [, , actorId] }) => { - const client = await this.createActorInspector(actorId); - const response = await client.rpcs.$get(); - - if (!response.ok) { - throw response; - } - return await response.json(); - }, - }); - }, - - actorClearEventsMutationOptions(actorId: ActorId) { - return { - mutationKey: [hash, "actor", actorId, "clear-events"], - mutationFn: async () => { - const client = await this.createActorInspector(actorId); - const response = await client.events.clear.$post(); - if (!response.ok) { - throw response; - } - return await response.json(); - }, - }; - }, - - actorWakeUpMutationOptions(actorId: ActorId) { - return { - mutationKey: [hash, "actor", actorId, "wake-up"], - mutationFn: async () => { - const client = await this.createActorInspector(actorId); - try { - await client.ping.$get(); - return true; - } catch { - return false; - } - }, - }; - }, - - actorAutoWakeUpQueryOptions( - actorId: ActorId, - { enabled }: { enabled?: boolean } = {}, - ) { - return queryOptions({ - enabled, - refetchInterval: 1000, - staleTime: 0, - gcTime: 0, - queryKey: [hash, "actor", actorId, "auto-wake-up"], - queryFn: async ({ queryKey: [, , actorId] }) => { - const client = await this.createActorInspector(actorId, { - auth: false, - }); - try { - await client.ping.$get(); - return true; - } catch { - return false; - } - }, - retry: false, - }); - }, -}); - -export type ActorContext = ReturnType; - -const ActorContext = createContext({} as ActorContext); - -export const useActor = () => useContext(ActorContext); - -export const ActorProvider = ActorContext.Provider; diff --git a/frontend/src/components/actors/actor-region.tsx b/frontend/src/components/actors/actor-region.tsx index 9a5f0b9a1c..348a5edaef 100644 --- a/frontend/src/components/actors/actor-region.tsx +++ b/frontend/src/components/actors/actor-region.tsx @@ -20,14 +20,14 @@ export function ActorRegion({ className, }: ActorRegionProps) { const { data: region } = useQuery( - useDataProvider().regionQueryOptions(regionId), + useDataProvider().datacenterQueryOptions(regionId), ); if (!regionId || !region) { return null; } - const regionKey = getRegionKey(region?.id); + const regionKey = getRegionKey(region?.name); if (showLabel) { return ( diff --git a/frontend/src/components/actors/actor-runtime.tsx b/frontend/src/components/actors/actor-runtime.tsx deleted file mode 100644 index c3803df811..0000000000 --- a/frontend/src/components/actors/actor-runtime.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { Suspense } from "react"; -import { Skeleton } from "../ui/skeleton"; -import { ActorBuild } from "./actor-build"; -import type { ActorId } from "./queries"; - -export interface ActorRuntimeProps { - actorId: ActorId; -} - -export function ActorRuntime({ actorId }: ActorRuntimeProps) { - return ( - }> - - - ); -} diff --git a/frontend/src/components/actors/actor-state-tab.tsx b/frontend/src/components/actors/actor-state-tab.tsx index 9a130da3f6..e5b65b7d7c 100644 --- a/frontend/src/components/actors/actor-state-tab.tsx +++ b/frontend/src/components/actors/actor-state-tab.tsx @@ -5,6 +5,7 @@ import { Button } from "../ui/button"; import { ActorEditableState } from "./actor-editable-state"; import { useActor } from "./actor-queries-context"; import { useActorsView } from "./actors-view-context-provider"; +import { useActorInspector } from "./inspector-context"; import type { ActorId } from "./queries"; interface ActorStateTabProps { @@ -14,12 +15,13 @@ interface ActorStateTabProps { export function ActorStateTab({ actorId }: ActorStateTabProps) { const { links } = useActorsView(); - const actorQueries = useActor(); + const actorQueries = useActorInspector(); + const { - data: state, + data: isStateEnabled, isError, isLoading, - } = useQuery(actorQueries.actorStateQueryOptions(actorId)); + } = useQuery(actorQueries.actorIsStateEnabledQueryOptions(actorId)); if (isError) { return ( @@ -35,7 +37,7 @@ export function ActorStateTab({ actorId }: ActorStateTabProps) { return Loading state...; } - if (!state?.enabled) { + if (!isStateEnabled) { return (

@@ -51,7 +53,7 @@ export function ActorStateTab({ actorId }: ActorStateTabProps) { return (

- +
); } diff --git a/frontend/src/components/actors/actor-status-label.tsx b/frontend/src/components/actors/actor-status-label.tsx index 2faac42c55..34ed06518a 100644 --- a/frontend/src/components/actors/actor-status-label.tsx +++ b/frontend/src/components/actors/actor-status-label.tsx @@ -48,17 +48,17 @@ export function QueriedActorStatusAdditionalInfo({ }: { actorId: ActorId; }) { - const { data: { rescheduleAt } = {} } = useQuery( + const { data: { rescheduleTs } = {} } = useQuery( useDataProvider().actorStatusAdditionalInfoQueryOptions(actorId), ); - if (rescheduleAt) { + if (rescheduleTs) { return ( Will try to start again{" "} - ( - {formatISO(rescheduleAt)}){" "} + ( + {formatISO(rescheduleTs)}){" "} ); diff --git a/frontend/src/components/actors/actor-stop-button.tsx b/frontend/src/components/actors/actor-stop-button.tsx index 978f906aec..46b786ee07 100644 --- a/frontend/src/components/actors/actor-stop-button.tsx +++ b/frontend/src/components/actors/actor-stop-button.tsx @@ -41,6 +41,7 @@ export function ActorStopButton({ actorId }: ActorStopButtonProps) { return ( { - const { data: features = [] } = useQuery( - useDataProvider().actorFeaturesQueryOptions(actorId), - ); - - const supportsConsole = features.includes(ActorFeature.Console); - return (
- {supportsConsole ? : null} +
@@ -80,15 +71,11 @@ function Console({ actorId }: { actorId: ActorId }) { ); } -export const ActorsActorEmptyDetails = ({ - features, -}: { - features: ActorFeature[]; -}) => { +export const ActorsActorEmptyDetails = () => { const { copy } = useActorsView(); return (
- +

{copy.selectActor}

@@ -100,7 +87,6 @@ export const ActorsActorEmptyDetails = ({ export function ActorTabs({ tab, - features, onTabChange, actorId, className, @@ -109,22 +95,12 @@ export function ActorTabs({ }: { disabled?: boolean; tab?: string; - features: ActorFeature[]; onTabChange?: (tab: string) => void; actorId?: ActorId; className?: string; children?: ReactNode; }) { - const supportsState = features?.includes(ActorFeature.State); - const supportsLogs = features?.includes(ActorFeature.Logs); - const supportsConnections = features?.includes(ActorFeature.Connections); - const supportsMetadata = features?.includes(ActorFeature.Config); - const supportsMetrics = features?.includes(ActorFeature.Metrics); - const supportsEvents = features?.includes(ActorFeature.EventsMonitoring); - const supportsDatabase = features?.includes(ActorFeature.Database); - - const defaultTab = supportsState ? "state" : "logs"; - const value = disabled ? undefined : tab || defaultTab; + const value = disabled ? undefined : tab || "state"; const guardContent = useInspectorGuard(); @@ -138,69 +114,59 @@ export function ActorTabs({
- {supportsState ? ( - - State - - ) : null} - {supportsConnections ? ( - - Connections - - ) : null} - {supportsEvents ? ( - - Events - - ) : null} - {supportsDatabase ? ( - - Database - - ) : null} - {supportsLogs ? ( - + State + + + + Connections + + + + Events + + + {/* + Database + */} + + {/* Logs - - ) : null} - {supportsMetadata ? ( - - Metadata - - ) : null} - {supportsMetrics ? ( - */} + + Metadata + + {/* Metrics - - ) : null} + */} {actorId ? ( {actorId ? ( <> - {supportsLogs ? ( - - }> - {guardContent || ( - - )} - - - ) : null} - {supportsMetadata ? ( - - - - ) : null} - {supportsConnections ? ( - - {guardContent || ( - - )} - - ) : null} - {supportsEvents ? ( - - {guardContent || ( - - )} - - ) : null} - {supportsDatabase ? ( - - {guardContent || ( - - )} - - ) : null} - {supportsState ? ( - - {guardContent || ( - - )} - - ) : null} - {supportsMetrics ? ( + + }> + {guardContent || } + + + + + + + {guardContent || ( + + )} + + + + {guardContent || } + + + {guardContent || } + + + {guardContent || } + + {/* {supportsMetrics ? ( )} - ) : null} + ) : null} */} ) : null} {children} diff --git a/frontend/src/components/actors/actors-list-row.tsx b/frontend/src/components/actors/actors-list-row.tsx index 22d70d1c1e..8fc5d29800 100644 --- a/frontend/src/components/actors/actors-list-row.tsx +++ b/frontend/src/components/actors/actors-list-row.tsx @@ -50,6 +50,7 @@ export const ActorsListRow = memo( {isVisible ? ( <> (null); diff --git a/frontend/src/components/actors/console/actor-console.tsx b/frontend/src/components/actors/console/actor-console.tsx index 02e0c328e2..e04b385c6e 100644 --- a/frontend/src/components/actors/console/actor-console.tsx +++ b/frontend/src/components/actors/console/actor-console.tsx @@ -3,8 +3,8 @@ import { useQuery } from "@tanstack/react-query"; import { AnimatePresence, motion } from "framer-motion"; import { useState } from "react"; import { Button, cn } from "@/components"; -import { useActor } from "../actor-queries-context"; import { useDataProvider } from "../data-provider"; +import { useActorInspector } from "../inspector-context"; import type { ActorId } from "../queries"; import { useActorWorkerStatus } from "../worker/actor-worker-context"; import { ActorWorkerStatus } from "../worker/actor-worker-status"; @@ -20,16 +20,15 @@ export function ActorConsole({ actorId }: ActorConsoleProps) { const status = useActorWorkerStatus(); const managerQueries = useDataProvider(); - const actorQueries = useActor(); + const actorInspector = useActorInspector(); const { data: { destroyedAt, sleepingAt } = {} } = useQuery( managerQueries.actorWorkerQueryOptions(actorId), ); - const { isSuccess, isError, isLoading } = useQuery( - actorQueries.actorPingQueryOptions(actorId, { - enabled: true, - refetchInterval: false, - }), - ); + const { isSuccess, isError, isLoading } = useQuery({ + ...actorInspector.actorPingQueryOptions(actorId), + enabled: true, + refetchInterval: false, + }); const isBlocked = status.type !== "ready" || !isSuccess || !!destroyedAt || !!sleepingAt; diff --git a/frontend/src/components/actors/crash-policy-select.tsx b/frontend/src/components/actors/crash-policy-select.tsx index b6dcc1b0e4..c5df9d6693 100644 --- a/frontend/src/components/actors/crash-policy-select.tsx +++ b/frontend/src/components/actors/crash-policy-select.tsx @@ -1,10 +1,12 @@ +import { Rivet } from "@rivetkit/engine-api-full"; import { Combobox } from "@/components"; -import { CrashPolicy } from "./queries"; -const VALUES = Array.from(Object.entries(CrashPolicy)).map(([key, value]) => ({ - label: key, - value, -})); +const VALUES = Array.from(Object.entries(Rivet.CrashPolicy)).map( + ([key, value]) => ({ + label: key, + value, + }), +); interface CrashPolicySelectProps { onValueChange: (value: string) => void; diff --git a/frontend/src/components/actors/dialogs/create-actor-dialog.tsx b/frontend/src/components/actors/dialogs/create-actor-dialog.tsx index c35e5d40c3..c9939e5e13 100644 --- a/frontend/src/components/actors/dialogs/create-actor-dialog.tsx +++ b/frontend/src/components/actors/dialogs/create-actor-dialog.tsx @@ -1,3 +1,4 @@ +import { Rivet } from "@rivetkit/engine-api-full"; import { useMutation } from "@tanstack/react-query"; import { useSearch } from "@tanstack/react-router"; import type { DialogContentProps } from "@/components/hooks"; @@ -18,7 +19,6 @@ import { Flex } from "../../ui/flex"; import { useActorsView } from "../actors-view-context-provider"; import { useDataProvider } from "../data-provider"; import * as ActorCreateForm from "../form/actor-create-form"; -import { CrashPolicy } from "../queries"; interface ContentProps extends DialogContentProps {} @@ -40,8 +40,10 @@ export default function CreateActorDialog({ onClose }: ContentProps) { name: values.name, input: values.input ? JSON.parse(values.input) : undefined, key: values.key, - datacenter: values.datacenter, - crashPolicy: values.crashPolicy || CrashPolicy.Restart, + datacenter: + __APP_TYPE__ === "inspector" ? "" : values.datacenter, + crashPolicy: + values.crashPolicy || Rivet.CrashPolicy.Restart, runnerNameSelector: values.runnerNameSelector || "default", }); onClose?.(); @@ -49,7 +51,7 @@ export default function CreateActorDialog({ onClose }: ContentProps) { defaultValues={{ name, key: getRandomKey(), - crashPolicy: CrashPolicy.Restart, + crashPolicy: Rivet.CrashPolicy.Restart, }} > diff --git a/frontend/src/components/actors/form/actor-create-form.tsx b/frontend/src/components/actors/form/actor-create-form.tsx index e82f71a316..8de2b9b95a 100644 --- a/frontend/src/components/actors/form/actor-create-form.tsx +++ b/frontend/src/components/actors/form/actor-create-form.tsx @@ -1,7 +1,5 @@ -import { - useInfiniteQuery, - useSuspenseInfiniteQuery, -} from "@tanstack/react-query"; +import { Rivet } from "@rivetkit/engine-api-full"; +import { useSuspenseInfiniteQuery } from "@tanstack/react-query"; import { useEffect, useRef } from "react"; import { type UseFormReturn, useFormContext } from "react-hook-form"; import z from "zod"; @@ -20,9 +18,7 @@ import { AllRunnerSelect } from "../all-runner-select"; import { BuildSelect } from "../build-select"; import { CrashPolicySelect } from "../crash-policy-select"; import { useEngineCompatDataProvider } from "../data-provider"; -import { CrashPolicy as CrashPolicyEnum } from "../queries"; import { RegionSelect } from "../region-select"; -import { ConnectedRunnerSelect } from "../runner-select"; const jsonValid = z.custom( (value) => { @@ -47,7 +43,7 @@ export const formSchema = z datacenter: z.string(), runnerNameSelector: z.string(), - crashPolicy: z.nativeEnum(CrashPolicyEnum), + crashPolicy: z.nativeEnum(Rivet.CrashPolicy), }) .partial({ datacenter: true, runnerNameSelector: true, crashPolicy: true }); @@ -222,7 +218,7 @@ export const PrefillActorName = () => { const { data: name, isSuccess } = useSuspenseInfiniteQuery({ ...useEngineCompatDataProvider().buildsQueryOptions(), - select: (data) => data.pages[0].builds[0].name, + select: (data) => Object.keys(data.pages[0].names)[0], }); const watchedValue = watch("name"); @@ -274,8 +270,9 @@ export const PrefillDatacenter = () => { ...useEngineCompatDataProvider().runnerConfigsQueryOptions(), select: (data) => { return Object.keys( - Object.values(data.pages[0].runnerConfigs)[0].datacenters, - )[0]; + Object.values(data.pages[0].runnerConfigs)[0]?.datacenters || + {}, + )?.[0]; }, }); diff --git a/frontend/src/components/actors/guard-connectable-inspector.tsx b/frontend/src/components/actors/guard-connectable-inspector.tsx index 22950a1145..a2d41405e9 100644 --- a/frontend/src/components/actors/guard-connectable-inspector.tsx +++ b/frontend/src/components/actors/guard-connectable-inspector.tsx @@ -6,20 +6,21 @@ import { useQuery, useSuspenseQuery, } from "@tanstack/react-query"; -import { useMatch, useRouteContext } from "@tanstack/react-router"; +import { useMatch, useRouteContext, useSearch } from "@tanstack/react-router"; import { createContext, type ReactNode, useContext, useMemo } from "react"; import { useLocalStorage } from "usehooks-ts"; -import { useInspectorCredentials } from "@/app/credentials-context"; -import { createInspectorActorContext } from "@/queries/actor-inspector"; -import { queryClient } from "@/queries/global"; import { DiscreteCopyButton } from "../copy-area"; import { getConfig } from "../lib/config"; import { ls } from "../lib/utils"; +import { ShimmerLine } from "../shimmer-line"; import { Button } from "../ui/button"; import { useFiltersValue } from "./actor-filters-context"; -import { ActorProvider, useActor } from "./actor-queries-context"; import { Info } from "./actor-state-tab"; import { useDataProvider, useEngineCompatDataProvider } from "./data-provider"; +import { + ActorInspectorProvider as InspectorProvider, + useActorInspector, +} from "./inspector-context"; import type { ActorId } from "./queries"; const InspectorGuardContext = createContext(null); @@ -35,17 +36,19 @@ export function GuardConnectableInspector({ actorId, children, }: GuardConnectableInspectorProps) { - const { data: { destroyedAt, pendingAllocationAt, startedAt } = {} } = - useQuery({ - ...useDataProvider().actorQueryOptions(actorId), - refetchInterval: 1000, - select: (data) => ({ - destroyedAt: data.destroyedAt, - sleepingAt: data.sleepingAt, - pendingAllocationAt: data.pendingAllocationAt, - startedAt: data.startedAt, - }), - }); + const filters = useFiltersValue({ onlyEphemeral: true }); + const { + data: { destroyedAt, pendingAllocationAt, startedAt, sleepingAt } = {}, + } = useQuery({ + ...useDataProvider().actorQueryOptions(actorId), + refetchInterval: 1000, + select: (data) => ({ + destroyedAt: data.destroyTs, + sleepingAt: data.sleepTs, + pendingAllocationAt: data.pendingAllocationTs, + startedAt: data.startTs, + }), + }); if (destroyedAt) { return ( @@ -65,6 +68,34 @@ export function GuardConnectableInspector({ ); } + if (sleepingAt) { + if (filters.wakeOnSelect?.value?.[0] === "1") { + return ( + + + + } + > + {children} + + ); + } + return ( + +

Unavailable for sleeping Actors.

+ + + } + > + {children} +
+ ); + } + return ( {children} @@ -103,36 +134,57 @@ function ActorContextProvider(props: { actorId: ActorId; children: ReactNode; }) { + const { data, isError } = useQuery( + useDataProvider().actorInspectorTokenQueryOptions(props.actorId), + ); + + if (isError || !data) { + return ( + +

Unable to retrieve the Actor's Inspector token.

+

+ Please verify that the Inspector is enabled for your + Actor and that you are using the latest version of + RivetKit. +

+ + } + > + {props.children} +
+ ); + } + return __APP_TYPE__ === "inspector" ? ( - + ) : ( - + ); } function ActorInspectorProvider({ actorId, + inspectorToken, children, }: { actorId: ActorId; + inspectorToken: string; children: ReactNode; }) { - const { credentials } = useInspectorCredentials(); - - if (!credentials?.url || !credentials?.token) { - throw new Error("Missing inspector credentials"); - } - - const actorContext = useMemo(() => { - return createInspectorActorContext({ - ...credentials, - }); - }, [credentials]); + const url = useSearch({ + from: "/_context", + select: (s) => s.u || "https://localhost:6420", + }); return ( - - {children} - + + {children} + ); } @@ -184,7 +236,7 @@ function useActorRunner({ actorId }: { actorId: ActorId }) { shouldThrow: false, }); - if (!match?.params.namespace || !actor.runner) { + if (!match?.params.namespace || !actor.runnerNameSelector) { throw new Error("Actor is missing required fields"); } @@ -194,7 +246,7 @@ function useActorRunner({ actorId }: { actorId: ActorId }) { isSuccess, } = useQuery({ ...useEngineCompatDataProvider().runnerByNameQueryOptions({ - runnerName: actor.runner, + runnerName: actor.runnerNameSelector, }), retryDelay: 10_000, refetchInterval: 1000, @@ -228,38 +280,31 @@ function useEngineToken() { function useActorEngineContext({ actorId }: { actorId: ActorId }) { const { actor, runner, isLoading } = useActorRunner({ actorId }); const engineToken = useEngineToken(); - const provider = useEngineCompatDataProvider(); - const actorContext = useMemo(() => { - return createInspectorActorContext({ + const credentials = useMemo(() => { + return { url: getConfig().apiUrl, - token: async () => { - const runner = await queryClient.fetchQuery( - provider.runnerByNameQueryOptions({ - runnerName: actor?.runner || "", - }), - ); - return (runner?.metadata?.inspectorToken as string) || ""; - }, - engineToken, - }); - }, [actor?.runner, provider.runnerByNameQueryOptions, engineToken]); + token: engineToken, + }; + }, [engineToken]); - return { actorContext, actor, runner, isLoading }; + return { actor, runner, isLoading, credentials }; } function ActorEngineProvider({ actorId, + inspectorToken, children, }: { actorId: ActorId; children: ReactNode; + inspectorToken: string; }) { - const { actorContext, actor } = useActorEngineContext({ + const { credentials, actor } = useActorEngineContext({ actorId, }); - if (!actor.runner) { + if (!actor.runnerNameSelector) { return ( } @@ -270,9 +315,12 @@ function ActorEngineProvider({ } return ( - - {children} - + + {children} + ); } @@ -295,11 +343,11 @@ function NoRunnerInfo({ runner }: { runner: string }) { } function WakeUpActorButton({ actorId }: { actorId: ActorId }) { - const actorContext = useActor(); + const actorInspector = useActorInspector(); const { runner } = useActorRunner({ actorId }); const { mutate, isPending } = useMutation( - actorContext.actorWakeUpMutationOptions(actorId), + actorInspector.actorWakeUpMutationOptions(actorId), ); if (!runner) return null; return ( @@ -316,21 +364,22 @@ function WakeUpActorButton({ actorId }: { actorId: ActorId }) { } function AutoWakeUpActor({ actorId }: { actorId: ActorId }) { - const actorContext = useActor(); + const actorInspector = useActorInspector(); const { actor, runner } = useActorRunner({ actorId }); - const { hasRunner } = useRunner(actor.runner); + const { hasRunner } = useRunner(actor.runnerNameSelector); useQuery( - actorContext.actorAutoWakeUpQueryOptions(actorId, { + actorInspector.actorAutoWakeUpQueryOptions(actorId, { enabled: hasRunner, }), ); - if (!hasRunner) return ; + if (!hasRunner) + return ; if (runner?.drainTs) - return ; + return ; return ( @@ -342,71 +391,9 @@ function AutoWakeUpActor({ actorId }: { actorId: ActorId }) { ); } -function InspectorGuard({ - actorId, - children, -}: { - actorId: ActorId; - children: ReactNode; -}) { - const filters = useFiltersValue({ onlyEphemeral: true }); - - const { data: { sleepingAt } = {} } = useQuery({ - ...useDataProvider().actorQueryOptions(actorId), - refetchInterval: 1000, - select: (data) => ({ - destroyedAt: data.destroyedAt, - sleepingAt: data.sleepingAt, - pendingAllocationAt: data.pendingAllocationAt, - startedAt: data.startedAt, - }), - }); - - if (sleepingAt) { - if (filters.wakeOnSelect?.value?.[0] === "1") { - return ( - - - - } - > - {children} - - ); - } - return ( - -

Unavailable for sleeping Actors.

- - - } - > - {children} -
- ); - } - return ( - {children} - ); -} - -function InspectorGuardInner({ - actorId, - children, -}: { - actorId: ActorId; - children: ReactNode; -}) { - const { isError } = useQuery({ - ...useActor().actorPingQueryOptions(actorId), - retryDelay: 10_000, - enabled: true, - }); - if (isError) { +function InspectorGuard({ children }: { children: ReactNode }) { + const { connectionStatus } = useActorInspector(); + if (connectionStatus === "error") { return ( + {connectionStatus !== "connected" && } + {children} + + ); } diff --git a/frontend/src/components/actors/index.ts b/frontend/src/components/actors/index.ts index bdf87a9973..4f39184f87 100644 --- a/frontend/src/components/actors/index.ts +++ b/frontend/src/components/actors/index.ts @@ -1,6 +1,4 @@ -export * from "./actor-context"; export * from "./actor-not-found"; -export * from "./actor-queries-context"; export * from "./actor-region"; export * from "./actor-status-indicator"; export * from "./actor-status-label"; diff --git a/frontend/src/components/actors/inspector-context.tsx b/frontend/src/components/actors/inspector-context.tsx new file mode 100644 index 0000000000..cce200a769 --- /dev/null +++ b/frontend/src/components/actors/inspector-context.tsx @@ -0,0 +1,540 @@ +import { + mutationOptions, + type QueryClient, + queryOptions, + useQueryClient, +} from "@tanstack/react-query"; +import * as cbor from "cbor-x"; +import { createContext, useContext, useMemo, useRef } from "react"; +import type ReconnectingWebSocket from "reconnectingwebsocket"; +import { + type Connection, + type Event, + type ToServer, + TO_CLIENT_VERSIONED as toClient, + TO_SERVER_VERSIONED as toServer, +} from "rivetkit/inspector"; +import { toast } from "sonner"; +import { match } from "ts-pattern"; +import { type ConnectionStatus, useWebSocket } from "../hooks/use-websocket"; +import type { ActorId } from "./queries"; + +export const actorInspectorQueriesKeys = { + actorState: (actorId: ActorId) => ["actor", actorId, "state"] as const, + actorIsStateEnabled: (actorId: ActorId) => + ["actor", actorId, "is-state-enabled"] as const, + actorConnections: (actorId: ActorId) => + ["actor", actorId, "connections"] as const, + actorDatabase: (actorId: ActorId) => + ["actor", actorId, "database"] as const, + actorEvents: (actorId: ActorId) => ["actor", actorId, "events"] as const, + actorRpcs: (actorId: ActorId) => ["actor", actorId, "rpcs"] as const, + actorClearEvents: (actorId: ActorId) => + ["actor", actorId, "clear-events"] as const, + actorWakeUp: (actorId: ActorId) => ["actor", actorId, "wake-up"] as const, + actorAutoWakeUp: (actorId: ActorId) => + ["actor", actorId, "auto-wake-up"] as const, +}; + +interface ActorInspectorApi { + ping: () => Promise; + executeAction: (name: string, args: unknown[]) => Promise; + patchState: (state: unknown) => Promise; + getConnections: () => Promise; + getEvents: () => Promise; + getState: () => Promise; +} + +export const createDefaultActorInspectorContext = ({ + api, +}: { + api: ActorInspectorApi; +}) => ({ + api, + actorStateQueryOptions(actorId: ActorId) { + return queryOptions({ + staleTime: Infinity, + queryKey: actorInspectorQueriesKeys.actorState(actorId), + queryFn: () => { + return api.getState(); + }, + }); + }, + + actorIsStateEnabledQueryOptions(actorId: ActorId) { + return queryOptions({ + staleTime: Infinity, + queryKey: actorInspectorQueriesKeys.actorIsStateEnabled(actorId), + queryFn: () => { + return false; + }, + }); + }, + + actorConnectionsQueryOptions(actorId: ActorId) { + return queryOptions({ + staleTime: Infinity, + queryKey: actorInspectorQueriesKeys.actorConnections(actorId), + queryFn: () => { + return api.getConnections(); + }, + }); + }, + + actorDatabaseQueryOptions(actorId: ActorId) { + // TODO: implement + return queryOptions({ + staleTime: Infinity, + queryKey: actorInspectorQueriesKeys.actorDatabase(actorId), + queryFn: () => { + return { enabled: false, db: [] } as unknown as { + enabled: boolean; + db: { + table: { name: string; type: string }; + records: number; + }[]; + }; + }, + }); + }, + + actorDatabaseEnabledQueryOptions(actorId: ActorId) { + // TODO: implement + return queryOptions({ + staleTime: Infinity, + ...this.actorDatabaseQueryOptions(actorId), + select: (data) => data.enabled, + }); + }, + + actorDatabaseTablesQueryOptions(actorId: ActorId) { + // TODO: implement + return queryOptions({ + ...this.actorDatabaseQueryOptions(actorId), + select: (data) => + data.db?.map((table) => ({ + name: table.table.name, + type: table.table.type, + records: table.records, + })) || [], + notifyOnChangeProps: ["data", "isError", "isLoading"], + }); + }, + + actorDatabaseRowsQueryOptions(actorId: ActorId, table: string) { + // TODO: implement + return queryOptions({ + staleTime: Infinity, + queryKey: [ + ...actorInspectorQueriesKeys.actorDatabase(actorId), + table, + ], + queryFn: () => { + return [] as unknown as Record[]; + }, + }); + }, + + actorEventsQueryOptions(actorId: ActorId) { + return queryOptions({ + staleTime: Infinity, + queryKey: actorInspectorQueriesKeys.actorEvents(actorId), + queryFn: () => { + return api.getEvents(); + }, + }); + }, + + actorRpcsQueryOptions(actorId: ActorId) { + return queryOptions({ + staleTime: Infinity, + queryKey: actorInspectorQueriesKeys.actorRpcs(actorId), + queryFn: () => { + return [] as string[]; + }, + }); + }, + + actorClearEventsMutationOptions(actorId: ActorId) { + return mutationOptions({ + mutationKey: ["actor", actorId, "clear-events"], + // TODO: + }); + }, + + actorWakeUpMutationOptions(actorId: ActorId) { + return mutationOptions({ + mutationKey: ["actor", actorId, "wake-up"], + // TODO: + }); + }, + + actorAutoWakeUpQueryOptions( + actorId: ActorId, + { enabled }: { enabled?: boolean } = {}, + ) { + return queryOptions({ + enabled, + refetchInterval: 1000, + staleTime: 0, + gcTime: 0, + queryKey: actorInspectorQueriesKeys.actorAutoWakeUp(actorId), + retry: false, + //FIXME: + }); + }, + + actorPingQueryOptions(actorId: ActorId) { + return queryOptions({ + queryKey: ["actor", actorId, "ping"], + queryFn: async () => { + try { + await api.ping(); + return true; + } catch { + return false; + } + }, + retry: false, + }); + }, + + actorStatePatchMutation(actorId: ActorId) { + return mutationOptions({ + mutationKey: ["actor", actorId, "state", "patch"], + mutationFn: async (state: unknown) => { + return api.patchState(state); + }, + }); + }, +}); + +export type ActorInspectorContext = ReturnType< + typeof createDefaultActorInspectorContext +> & { connectionStatus: ConnectionStatus }; + +const ActorInspectorContext = createContext({} as ActorInspectorContext); + +export const useActorInspector = () => useContext(ActorInspectorContext); + +export const ActorInspectorProvider = ({ + children, + actorId, + credentials, +}: { + children: React.ReactNode; + actorId: ActorId; + credentials: { url: string; inspectorToken: string; token: string }; +}) => { + const protocols = useMemo( + () => + [ + "rivet", + `rivet_target.actor`, + `rivet_actor.${actorId}`, + `rivet_encoding.bare`, + credentials.token ? `rivet_token.${credentials.token}` : "", + credentials.inspectorToken + ? `rivet_inspector_token.${credentials.inspectorToken}` + : "", + ].filter(Boolean), + [actorId, credentials.token, credentials.inspectorToken], + ); + + const queryClient = useQueryClient(); + + const actionsManager = useRef(new ActionsManager()); + + const onMessage = useMemo(() => { + return createMessageHandler({ queryClient, actorId, actionsManager }); + }, [queryClient, actorId]); + + const { sendMessage, reconnect, status } = useWebSocket( + new URL(`/gateway/${actorId}/inspector/connect`, credentials.url).href, + protocols, + { onMessage }, + ); + + const api = useMemo(() => { + return { + ping: async () => { + return reconnect(); + }, + executeAction: async (name, args) => { + const { id, promise } = + actionsManager.current.createSuspension(); + + sendMessage( + serverMessage({ + body: { + tag: "ActionRequest", + val: { + id: BigInt(id), + name, + args: new Uint8Array(cbor.encode(args)).buffer, + }, + }, + }), + ); + + return promise; + }, + + patchState: async (state) => { + sendMessage( + serverMessage({ + body: { + tag: "PatchStateRequest", + val: { + state: new Uint8Array(cbor.encode(state)) + .buffer, + }, + }, + }), + ); + }, + + getConnections: async () => { + const { id, promise } = + actionsManager.current.createSuspension(); + + sendMessage( + serverMessage({ + body: { + tag: "ConnectionsRequest", + val: { id: BigInt(id) }, + }, + }), + ); + + return promise; + }, + + getEvents: async () => { + const { id, promise } = + actionsManager.current.createSuspension(); + + sendMessage( + serverMessage({ + body: { + tag: "EventsRequest", + val: { id: BigInt(id) }, + }, + }), + ); + + return promise; + }, + + getState: async () => { + const { id, promise } = + actionsManager.current.createSuspension(); + + sendMessage( + serverMessage({ + body: { + tag: "StateRequest", + val: { id: BigInt(id) }, + }, + }), + ); + + return promise; + }, + } satisfies ActorInspectorApi; + }, [sendMessage, reconnect]); + + const value = useMemo(() => { + return { + connectionStatus: status, + ...createDefaultActorInspectorContext({ + api, + }), + }; + }, [api, status]); + + return ( + + {children} + + ); +}; + +const createMessageHandler = + ({ + queryClient, + actorId, + actionsManager, + }: { + queryClient: QueryClient; + actorId: ActorId; + actionsManager: React.RefObject; + }) => + async (e: ReconnectingWebSocket.MessageEvent) => { + const message = toClient.deserializeWithEmbeddedVersion( + new Uint8Array(await e.data.arrayBuffer()), + ); + + match(message.body) + .with({ tag: "Init" }, (body) => { + queryClient.setQueryData( + actorInspectorQueriesKeys.actorState(actorId), + transformState(body.val.state), + ); + + queryClient.setQueryData( + actorInspectorQueriesKeys.actorConnections(actorId), + transformConnections(body.val.connections), + ); + + queryClient.setQueryData( + actorInspectorQueriesKeys.actorEvents(actorId), + transformEvents(body.val.events), + ); + + queryClient.setQueryData( + actorInspectorQueriesKeys.actorIsStateEnabled(actorId), + body.val.isStateEnabled, + ); + }) + .with({ tag: "ConnectionsResponse" }, (body) => { + const { rid } = body.val; + actionsManager.current.resolveSuspension( + Number(rid), + transformConnections(body.val.connections), + ); + }) + .with({ tag: "EventsResponse" }, (body) => { + const { rid } = body.val; + actionsManager.current.resolveSuspension( + Number(rid), + transformEvents(body.val.events), + ); + }) + .with({ tag: "StateResponse" }, (body) => { + const { rid } = body.val; + actionsManager.current.resolveSuspension( + Number(rid), + cbor.decode(new Uint8Array(body.val.state)), + ); + }) + .with({ tag: "ActionResponse" }, (body) => { + const { rid } = body.val; + actionsManager.current.resolveSuspension( + Number(rid), + cbor.decode(new Uint8Array(body.val.output)), + ); + }) + .with({ tag: "ConnectionsUpdated" }, (body) => { + queryClient.setQueryData( + actorInspectorQueriesKeys.actorConnections(actorId), + transformConnections(body.val.connections), + ); + }) + .with({ tag: "StateUpdated" }, (body) => { + queryClient.setQueryData( + actorInspectorQueriesKeys.actorState(actorId), + transformState(body.val.state), + ); + }) + .with({ tag: "EventsUpdated" }, (body) => { + queryClient.setQueryData( + actorInspectorQueriesKeys.actorEvents(actorId), + transformEvents(body.val.events), + ); + }) + .with({ tag: "Error" }, (body) => { + toast.error(`Inspector error: ${body.val.message}`); + }) + .exhaustive(); + }; + +function transformEvents(events: readonly Event[]) { + return events.map((event) => { + const base = { + ...event, + timestamp: new Date(Number(event.timestamp)), + }; + + return match(event.body) + .with({ tag: "FiredEvent" }, (body) => ({ + ...base, + body: { + ...body, + val: { + ...body.val, + args: cbor.decode(new Uint8Array(body.val.args)), + }, + }, + })) + .with({ tag: "ActionEvent" }, (body) => ({ + ...base, + body: { + ...body, + val: { + ...body.val, + args: cbor.decode(new Uint8Array(body.val.args)), + }, + }, + })) + .with({ tag: "BroadcastEvent" }, (body) => ({ + ...base, + body: { + ...body, + val: { + ...body.val, + args: cbor.decode(new Uint8Array(body.val.args)), + }, + }, + })) + .with({ tag: "SubscribeEvent" }, (body) => ({ + ...base, + body: { + ...body, + }, + })) + .with({ tag: "UnSubscribeEvent" }, (body) => ({ + ...base, + body: { + ...body, + }, + })) + .exhaustive(); + }); +} + +function transformConnections(connections: readonly Connection[]) { + return connections.map((connection) => ({ + ...connection, + details: cbor.decode(new Uint8Array(connection.details)), + })); +} + +function transformState(state: ArrayBuffer) { + return cbor.decode(new Uint8Array(state)); +} + +function serverMessage(data: ToServer) { + return toServer.serializeWithEmbeddedVersion(data); +} + +class ActionsManager { + private suspensions = new Map>(); + + private nextId = 1; + + createSuspension(): { id: number; promise: Promise } { + const id = this.nextId++; + const { promise, resolve, reject } = Promise.withResolvers(); + this.suspensions.set(id, { promise, resolve, reject }); + return { id, promise }; + } + + resolveSuspension(id: number, value?: unknown) { + const suspension = this.suspensions.get(id); + if (suspension) { + suspension.resolve(value); + this.suspensions.delete(id); + } + } +} diff --git a/frontend/src/components/actors/queries/actor.ts b/frontend/src/components/actors/queries/actor.ts deleted file mode 100644 index 0ca413fa21..0000000000 --- a/frontend/src/components/actors/queries/actor.ts +++ /dev/null @@ -1,232 +0,0 @@ -import { fetchEventSource } from "@microsoft/fetch-event-source"; -import { useMutation, useQueryClient } from "@tanstack/react-query"; -import { compare } from "fast-json-patch"; -import { useCallback, useEffect } from "react"; -import type { ActorId, RecordedRealtimeEvent } from "rivetkit/inspector"; -import { useAsyncMemo } from "@/components/hooks/use-async-memo"; -import { useActor } from "../actor-queries-context"; - -export const useActorClearEventsMutation = ( - actorId: ActorId, - options?: Parameters[1], -) => { - const queryClient = useQueryClient(); - const queries = useActor(); - return useMutation({ - ...queries.actorClearEventsMutationOptions(actorId), - onMutate: async () => { - queryClient.setQueryData( - queries.actorEventsQueryOptions(actorId).queryKey, - () => ({ events: [] }), - ); - }, - ...options, - }); -}; - -export const useActorStatePatchMutation = ( - actorId: ActorId, - options?: Parameters[1], -) => { - const queryClient = useQueryClient(); - const queries = useActor(); - return useMutation({ - mutationFn: async (data: any) => { - const client = await queries.createActorInspector(actorId); - - const oldStateQuery = queryClient.getQueryData( - queries.actorStateQueryOptions(actorId).queryKey, - ); - - const oldState = oldStateQuery?.state; - - let response: Awaited>; - - if (!oldState || !isPatchable(data)) { - response = await client.state.$patch({ - // its okay, we know the type - json: { replace: data }, - }); - } else { - const patches = compare(oldState, data); - response = await client.state.$patch({ - // its okay, we know the type - // @ts-expect-error - json: { patch: patches }, - }); - } - - if (!response.ok) { - throw response; - } - return (await response.json()) as { - state: unknown; - enabled: boolean; - }; - }, - onSuccess: (data) => { - queryClient.setQueryData( - queries.actorStateQueryOptions(actorId).queryKey, - () => ({ enabled: true, state: data.state }), - ); - }, - ...options, - }); -}; - -const getHeaders = ( - v: - | Record - | (() => Record | Promise>), -) => { - if (typeof v === "function") { - return v(); - } - return v; -}; - -function useStream( - actorId: ActorId, - onMessage: (data: T) => void, - url: string | null | undefined, - opts: { enabled: boolean } = { enabled: true }, -) { - const stableOnMessage = useCallback(onMessage, []); - const queries = useActor(); - - useEffect(() => { - const controller = new AbortController(); - if (!opts.enabled || !url) { - controller.abort(); - return () => controller.abort(); - } - - async function establishConnection() { - if (!url) { - return; - } - fetchEventSource(url, { - signal: controller.signal, - headers: await getHeaders( - ( - await queries.createActorInspectorFetchConfiguration( - actorId, - ) - )?.headers || {}, - ), - onmessage: (event) => { - const msg = JSON.parse(event.data); - stableOnMessage(msg); - }, - onclose: async () => { - await new Promise((resolve) => setTimeout(resolve, 1000)); - controller.signal.throwIfAborted(); - establishConnection(); - }, - }).catch((error) => console.error(error)); - } - - establishConnection(); - return () => { - controller.abort(); - }; - }, [ - url, - actorId, - opts.enabled, - stableOnMessage, - queries.createActorInspectorFetchConfiguration, - ]); -} - -export const useActorStateStream = ( - actorId: ActorId, - opts: { enabled: boolean } = { enabled: true }, -) => { - const queryClient = useQueryClient(); - const queries = useActor(); - - useStream( - actorId, - useCallback( - (data: unknown) => { - queryClient.setQueryData( - queries.actorStateQueryOptions(actorId).queryKey, - () => ({ enabled: true, state: data }), - ); - }, - [queryClient, actorId, queries], - ), - useAsyncMemo( - async () => - ( - await queries.createActorInspector(actorId) - ).state.stream.$url().href, - [actorId, queries], - ), - opts, - ); -}; - -export const useActorConnectionsStream = (actorId: ActorId) => { - const queryClient = useQueryClient(); - const queries = useActor(); - - useStream( - actorId, - useCallback( - (data) => { - queryClient.setQueryData( - queries.actorConnectionsQueryOptions(actorId).queryKey, - () => ({ enabled: true, connections: data }), - ); - }, - [queryClient, actorId, queries], - ), - useAsyncMemo( - async () => - ( - await queries.createActorInspector(actorId) - ).connections.stream.$url().href, - [actorId, queries], - ), - ); -}; - -export const useActorEventsStream = ( - actorId: ActorId, - opts: { enabled: boolean }, -) => { - const queryClient = useQueryClient(); - const queries = useActor(); - - useStream( - actorId, - useCallback( - (data: RecordedRealtimeEvent[]) => { - queryClient.setQueryData( - queries.actorEventsQueryOptions(actorId).queryKey, - () => { - return { events: data }; - }, - ); - }, - [queryClient, actorId, queries], - ), - useAsyncMemo( - async () => - ( - await queries.createActorInspector(actorId) - ).events.stream.$url().href, - [actorId, queries], - ), - opts, - ); -}; - -/** - * Check if the object is patchable, i.e. if it is an object and not null. - */ -function isPatchable(data: unknown) { - return typeof data === "object" && data !== null; -} diff --git a/frontend/src/components/actors/queries/index.ts b/frontend/src/components/actors/queries/index.ts index 383c469228..47f1d4f6df 100644 --- a/frontend/src/components/actors/queries/index.ts +++ b/frontend/src/components/actors/queries/index.ts @@ -1,95 +1,6 @@ -import type { Actor as InspectorActor } from "rivetkit/inspector"; +import type { Rivet } from "@rivetkit/engine-api-full"; -export type { ActorLogEntry } from "rivetkit/inspector"; -export { ActorFeature } from "rivetkit/inspector"; - -import type { ActorId } from "rivetkit/inspector"; - -export type { ActorId }; - -export type PortRouting = { - guard?: {}; - host?: {}; -}; - -export type Port = { - protocol: "http" | "https" | "tcp" | "tcp_tls" | "udp"; - internalPort?: number; - hostname?: string; - port?: number; - path?: string; - /** Fully formed connection URL including protocol, hostname, port, and path, if applicable. */ - url?: string; - routing: PortRouting; -}; - -export type Runtime = { - build: string; - arguments?: string[]; - environment?: Record; -}; - -export type Lifecycle = { - /** The duration to wait for in milliseconds before killing the actor. This should be set to a safe default, and can be overridden during a DELETE request if needed. */ - killTimeout?: number; - /** If true, the actor will try to reschedule itself automatically in the event of a crash or a datacenter failover. The actor will not reschedule if it exits successfully. */ - durable?: boolean; -}; - -export type Resources = { - /** - * The number of CPU cores in millicores, or 1/1000 of a core. For example, - * 1/8 of a core would be 125 millicores, and 1 core would be 1000 - * millicores. - */ - cpu: number; - /** The amount of memory in megabytes */ - memory: number; -}; - -export type Actor = Omit & { - network?: { - mode: "bridge" | "host"; - ports: Record; - }; - runtime?: Runtime; - lifecycle?: Lifecycle; - key: string | undefined; - - // engine related - runner?: string; - crashPolicy?: CrashPolicy; - sleepingAt?: string | null; - connectableAt?: string | null; - pendingAllocationAt?: string | null; - datacenter?: string | null; - rescheduleAt?: string | null; -} & { id: ActorId }; - -export enum CrashPolicy { - Restart = "restart", - Sleep = "sleep", - Destroy = "destroy", -} - -export type ActorMetrics = { - metrics: Record; - rawData: Record; - interval: number; -}; - -export type Build = { - id: string; - name: string; -}; - -export type Region = { - id: string; - name: string; - url?: string; -}; - -export * from "./actor"; +export type ActorId = string; export type ActorStatus = | "starting" @@ -103,49 +14,49 @@ export type ActorStatus = export function getActorStatus( actor: Pick< - Actor, - | "createdAt" - | "startedAt" - | "destroyedAt" - | "sleepingAt" - | "pendingAllocationAt" - | "rescheduleAt" + Rivet.Actor, + | "createTs" + | "destroyTs" + | "sleepTs" + | "pendingAllocationTs" + | "rescheduleTs" + | "connectableTs" >, ): ActorStatus { const { - createdAt, - startedAt, - destroyedAt, - sleepingAt, - pendingAllocationAt, - rescheduleAt, + createTs, + connectableTs, + destroyTs, + sleepTs, + pendingAllocationTs, + rescheduleTs, } = actor; - if (rescheduleAt) { + if (rescheduleTs) { return "crash-loop"; } - if (pendingAllocationAt && !startedAt && !destroyedAt) { + if (pendingAllocationTs && !connectableTs && !destroyTs) { return "pending"; } - if (createdAt && sleepingAt && !destroyedAt) { + if (createTs && sleepTs && !destroyTs) { return "sleeping"; } - if (createdAt && !startedAt && !destroyedAt) { + if (createTs && !connectableTs && !destroyTs) { return "starting"; } - if (createdAt && startedAt && !destroyedAt) { + if (createTs && connectableTs && !destroyTs) { return "running"; } - if (createdAt && startedAt && destroyedAt) { + if (createTs && connectableTs && destroyTs) { return "stopped"; } - if (createdAt && !startedAt && destroyedAt) { + if (createTs && !connectableTs && destroyTs) { return "crashed"; } diff --git a/frontend/src/components/actors/region-select.tsx b/frontend/src/components/actors/region-select.tsx index bb8dee1831..40cc2ee55e 100644 --- a/frontend/src/components/actors/region-select.tsx +++ b/frontend/src/components/actors/region-select.tsx @@ -19,7 +19,7 @@ export function RegionSelect({ fetchNextPage, isLoading, isFetchingNextPage, - } = useInfiniteQuery(useDataProvider().regionsQueryOptions()); + } = useInfiniteQuery(useDataProvider().datacentersQueryOptions()); const regions = [ ...(showAuto @@ -33,8 +33,8 @@ export function RegionSelect({ : []), ...data.map((region) => { return { - label: , - value: region.id, + label: , + value: region.name, region, }; }), @@ -50,10 +50,7 @@ export function RegionSelect({ onLoadMore={fetchNextPage} filter={(option, searchMixed) => { const search = searchMixed.toLowerCase(); - return ( - option.region.id.includes(search) || - option.region.name.includes(search) - ); + return option.region.name.toLowerCase().includes(search); }} className="w-full" /> diff --git a/frontend/src/components/actors/worker/actor-repl.worker.ts b/frontend/src/components/actors/worker/actor-repl.worker.ts index 51d6049937..e946342dc1 100644 --- a/frontend/src/components/actors/worker/actor-repl.worker.ts +++ b/frontend/src/components/actors/worker/actor-repl.worker.ts @@ -1,12 +1,10 @@ import { fromJs } from "esast-util-from-js"; import { toJs } from "estree-util-to-js"; -import { createActorInspectorClient } from "rivetkit/inspector"; import { createHighlighterCore, createOnigurumaEngine, type HighlighterCore, } from "shiki"; -import { match } from "ts-pattern"; import { type InitMessage, MessageSchema, @@ -104,6 +102,9 @@ const createConsole = (id: string) => { }; let init: null | Omit = null; +let actionIdCounter = 0; + +const actions = new Map>(); addEventListener("message", async (event) => { const { success, error, data } = MessageSchema.safeParse(event.data); @@ -121,6 +122,21 @@ addEventListener("message", async (event) => { return; } + if (data.type === "actionResponse") { + const action = actions.get(data.id); + if (!action) { + console.error("No such action", data.id); + return; + } + if (data.data.success) { + action.resolve(data.data.result); + } else { + action.reject(new Error(data.data.error || "Unknown error")); + } + actions.delete(data.id); + return; + } + if (data.type === "code") { const actor = init; if (!actor) { @@ -177,44 +193,21 @@ function respond(msg: Response) { async function callAction({ name, args }: { name: string; args: unknown[] }) { if (!init) throw new Error("Actor not initialized"); - const url = new URL(`inspect`, init.endpoint).href; - - const additionalHeaders = match(__APP_TYPE__) - .with("engine", () => { - return init?.engineToken - ? { "X-Rivet-Token": init.engineToken || "" } - : ({} as Record); - }) - .otherwise(() => ({})); - - // we need to build this from scratch because we don't have access to - // createInspectorActorContext in the worker - // and we want to avoid bundling the entire RivetKit here, issues with @react-refresh - const client = createActorInspectorClient(url, { - headers: { - Authorization: init.inspectorToken - ? `Bearer ${init.inspectorToken}` - : "", - "x-rivet-target": "actor", - "x-rivet-actor": init.id, - "X-RivetKit-Query": JSON.stringify({ - getForId: { actorId: init.id }, - }), - ...additionalHeaders, - }, - }); + const actionId = actionIdCounter++; - const response = await client.action.$post({ - json: { name, params: args }, + const { promise, resolve, reject } = Promise.withResolvers(); + actions.set(actionId, { promise, resolve, reject }); + + respond({ + type: "invokeAction", + id: actionId, + data: { name, args }, }); - if (!response.ok) { - try { - return await response.json(); - } catch { - return await response.text(); - } + try { + const result = await promise; + return result; + } finally { + actions.delete(actionId); } - - return (await response.json()).result; } diff --git a/frontend/src/components/actors/worker/actor-worker-container.ts b/frontend/src/components/actors/worker/actor-worker-container.ts index 3a1a580eb8..b46adae760 100644 --- a/frontend/src/components/actors/worker/actor-worker-container.ts +++ b/frontend/src/components/actors/worker/actor-worker-container.ts @@ -1,6 +1,7 @@ import { CancelledError } from "@tanstack/react-query"; import ActorWorker from "./actor-repl.worker?worker"; import { + type ActionResponse, type CodeMessage, type FormattedCode, type InitMessage, @@ -41,7 +42,8 @@ interface Meta { engineToken?: string | (() => Promise) | (() => string); runnerName?: string; namespace?: string; - inspectorToken?: string; + inspectorToken?: string | null; + invokeAction?: (action: string, params: unknown[]) => Promise; } export class ActorWorkerContainer { @@ -70,11 +72,6 @@ export class ActorWorkerContainer { try { signal.throwIfAborted(); - // FIXME(RVT-4553) - // if (actor.resources.cpu !== 125 || actor.resources.memory !== 128) { - // throw new Error("Unsupported actor resources"); - // } - // If we reached this point, the actor is supported // check if we still operate on the same actor if (this.#meta.actorId !== meta.actorId) { @@ -263,6 +260,25 @@ export class ActorWorkerContainer { } } + if (msg.type === "invokeAction") { + this.#meta + ?.invokeAction?.(msg.data.name, msg.data.args) + .then((result) => { + this.#worker?.postMessage({ + type: "actionResponse", + id: msg.id, + data: { success: true, result }, + } satisfies ActionResponse); + }) + .catch((error) => { + this.#worker?.postMessage({ + type: "actionResponse", + id: msg.id, + data: { success: false, error: String(error) }, + } satisfies ActionResponse); + }); + } + if (msg.type === "ready") { this.#state.status = { type: "ready" }; this.#update(); diff --git a/frontend/src/components/actors/worker/actor-worker-context.tsx b/frontend/src/components/actors/worker/actor-worker-context.tsx index 82969046eb..5f8f87bec2 100644 --- a/frontend/src/components/actors/worker/actor-worker-context.tsx +++ b/frontend/src/components/actors/worker/actor-worker-context.tsx @@ -1,3 +1,4 @@ +/** biome-ignore-all lint/correctness/useHookAtTopLevel: guarded by build constant */ import { useQuery } from "@tanstack/react-query"; import { createContext, @@ -9,11 +10,10 @@ import { useSyncExternalStore, } from "react"; import { match } from "ts-pattern"; -import { useInspectorCredentials } from "@/app/credentials-context"; -import { assertNonNullable, ls } from "../../lib/utils"; -import { useActor } from "../actor-queries-context"; +import { assertNonNullable } from "../../lib/utils"; import { useDataProvider, useEngineCompatDataProvider } from "../data-provider"; -import { ActorFeature, type ActorId } from "../queries"; +import { useActorInspector } from "../inspector-context"; +import type { ActorId } from "../queries"; import { ActorWorkerContainer } from "./actor-worker-container"; export const ActorWorkerContext = createContext( @@ -26,26 +26,6 @@ export const useActorWorker = () => { return value; }; -const useInspectorToken = (runnerName: string) => { - return match(__APP_TYPE__) - .with("inspector", () => { - // biome-ignore lint/correctness/useHookAtTopLevel: guarded by build constant - const inspectorCreds = useInspectorCredentials(); - return inspectorCreds.credentials?.token; - }) - .otherwise(() => { - // biome-ignore lint/correctness/useHookAtTopLevel: guarded by build constant - const provider = useEngineCompatDataProvider(); - // biome-ignore lint/correctness/useHookAtTopLevel: guarded by build constant - const { data } = useQuery( - provider.runnerByNameQueryOptions({ - runnerName, - }), - ); - return (data?.metadata?.inspectorToken as string) || ""; - }); -}; - const useConnectionDetails = () => { return match(__APP_TYPE__) .with("inspector", () => { @@ -74,7 +54,6 @@ export const ActorWorkerContextProvider = ({ const { data: { - features, name, endpoint, destroyedAt, @@ -83,19 +62,17 @@ export const ActorWorkerContextProvider = ({ runner, } = {}, } = useQuery(dataProvider.actorWorkerQueryOptions(actorId)); - const inspectorToken = useInspectorToken(runner || ""); + const { data: inspectorToken } = useQuery( + useDataProvider().actorInspectorTokenQueryOptions(actorId), + ); - const enabled = - (features?.includes(ActorFeature.Console) && - !destroyedAt && - !sleepingAt && - !!startedAt) ?? - false; + const enabled = (!destroyedAt && !sleepingAt && !!startedAt) ?? false; - const actorQueries = useActor(); - const { data: { rpcs } = {} } = useQuery( - actorQueries.actorRpcsQueryOptions(actorId, { enabled }), - ); + const actorInspector = useActorInspector(); + const { data: rpcs = [] } = useQuery({ + ...actorInspector.actorRpcsQueryOptions(actorId), + enabled, + }); const [container] = useState( () => new ActorWorkerContainer(), @@ -116,6 +93,7 @@ export const ActorWorkerContextProvider = ({ runnerName: runner, namespace, inspectorToken, + invokeAction: actorInspector.api.executeAction, }); } @@ -133,6 +111,7 @@ export const ActorWorkerContextProvider = ({ inspectorToken, namespace, runner, + actorInspector.api.executeAction, ]); return ( diff --git a/frontend/src/components/actors/worker/actor-worker-schema.ts b/frontend/src/components/actors/worker/actor-worker-schema.ts index a90aee8ad0..1226b9d93e 100644 --- a/frontend/src/components/actors/worker/actor-worker-schema.ts +++ b/frontend/src/components/actors/worker/actor-worker-schema.ts @@ -18,12 +18,23 @@ const InitMessageSchema = z.object({ engineToken: z.string().optional(), namespace: z.string().optional(), runnerName: z.string().optional(), - inspectorToken: z.string().optional(), + inspectorToken: z.string().nullish(), +}); + +const ActionResponseSchema = z.object({ + type: z.literal("actionResponse"), + id: z.number(), + data: z.object({ + result: z.any().optional(), + error: z.string().optional(), + success: z.boolean(), + }), }); export const MessageSchema = z.discriminatedUnion("type", [ CodeMessageSchema, InitMessageSchema, + ActionResponseSchema, ]); export const FormattedCodeSchema = z @@ -70,6 +81,14 @@ export const ResponseSchema = z.discriminatedUnion("type", [ z.object({ type: z.literal("ready"), }), + z.object({ + type: z.literal("invokeAction"), + id: z.number(), + data: z.object({ + name: z.string(), + args: z.array(z.any()), + }), + }), ]); export type Response = z.infer; @@ -78,3 +97,4 @@ export type FormattedCode = z.infer; export type Log = z.infer; export type InitMessage = z.infer; export type CodeMessage = z.infer; +export type ActionResponse = z.infer; diff --git a/frontend/src/components/connection-form.tsx b/frontend/src/components/connection-form.tsx index 4c9fb60019..cc1e00197a 100644 --- a/frontend/src/components/connection-form.tsx +++ b/frontend/src/components/connection-form.tsx @@ -1,8 +1,13 @@ import type { ComponentProps } from "react"; import z from "zod"; import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, Button, createSchemaForm, + FormDescription, FormField, FormItem, FormLabel, @@ -11,11 +16,7 @@ import { } from "@/components"; const connectionFormSchema = z.object({ - username: z - .string() - .url("Please enter a valid URL") - .min(1, "URL is required"), - token: z.string().min(1, "Token is required"), + url: z.string().url("Please enter a valid URL").min(1, "URL is required"), }); const { Form, Submit: ConnectionSubmit } = @@ -28,7 +29,7 @@ export const ConnectionForm = (
( Endpoint @@ -41,20 +42,35 @@ export const ConnectionForm = ( )} /> - ( - - Token - + + + Advanced + + + + ( + + Token + + + Connecting to Rivet Engine? You will + need to provide an access token + here. + + + + )} /> - - - )} - /> + + + */}