rustploy-gui 0.1.4

Rustploy — desktop client (glacier-ui) for the Rustploy PaaS daemon
--!strict
-- state.luau — estado mutável compartilhado entre todos os handlers/*.luau
-- (mesmo interpretador, mesma tabela: `require("../state")` é cacheado por
-- identidade — pelo CAMINHO resolvido do arquivo, não pela string pedida —
-- então toda mutação feita por um handler é vista pelos demais).
--
-- Convenção de `require` neste projeto (glacier-ui 0.22.0+: resolução
-- relativa ao ARQUIVO que chama, como Node.js/Lua padrão — ver
-- CHANGELOG do glacier-ui e docs/luau-modularizacao-pacotes.md):
--   - Irmão no MESMO diretório/pacote → nome nu: `require("stream")` de
--     dentro de `handlers/` acha `handlers/stream.luau`.
--   - Pacote PAI (este arquivo, `helpers.luau`, `format.luau`, `net/api.luau`,
--     todos na raiz de `views/scripts/`) a partir de dentro de `handlers/`
--     ou `format/` → prefixo explícito `../`: `require("../state")`.
--   - Do script de ENTRADA (`app.luau`, que fica na raiz) → caminho completo
--     nu: `require("handlers/connection")`, `require("format/time")`.
-- Ver `docs/luau-modularizacao-pacotes.md` (raiz do repo rustploy) para a
-- investigação completa que motivou essa convenção.

local Api = require("net/api")

-- `name` é capturado ao armar o deploy (start_deploy) só para a notificação do
-- SO no desfecho: o evento DeployStateChanged identifica o serviço por
-- `service_id`, não pelo nome. (O evento NÃO é magro como este comentário já
-- afirmou: além de `deployment_id`/`service_id`/`state`/`timestamp`, ele traz
-- `message` — o motivo da falha — que o handler de stream usa na notificação e
-- no toast. Ver docs/plano-erro-de-deploy-invisivel.md.)
export type DeployTrack = {
    service_id: string,
    deployment_id: string,
    running: boolean,
    name: string
}

export type State = {
    api: Api.Client?,                 -- cliente Api conectado
    state_conn: any,                  -- handle do stream SSE
    snap: any,                        -- último snapshot decodificado (para refiltrar)
    last_pairs: { any },               -- serviços normalizados { svc, proj } do snapshot
    metrics_by_id: { [string]: any },  -- service_id → ContainerMetricsPoint (bus)
    search_term: string,              -- termo de busca corrente (minúsculas)
    -- Logs (runtime/build) NÃO vivem mais aqui: cada tipo roda numa janela
    -- isolada (log_window.luau) com seu próprio ring e SSE dedicada, para não
    -- pesar na reavaliação da árvore da janela principal. Ver handlers/services
    -- (open_logs_window / deployment_logs) e o daemon (endpoints /api/.../logs).
    deploy_track: DeployTrack,
    new_service_templates_all: { any },         -- catálogo completo de templates (filtrar busca)
    jobs_by_id: { [string]: any },     -- job_id → Job cru (do snapshot) — job_toggle precisa do compose/name/main_service completos pra reenviar o JobUpdate.
    jobs_inflight: { [string]: boolean }, -- job_id → true entre o click em "Rodar agora" e o refresh_now() que confirma o novo job_run — trava double-click durante o round-trip da RPC (o botão só reflete `running` do snapshot DEPOIS disso).
    docker_cleanup_last_run_at_raw: any,           -- DockerCleanupConfig.last_run_at cru (ISO8601 ou nil), round-trip opaco: preserva o horário real entre saves do formulário de Manutenção, pra um agendamento IntervalHours não reiniciar a contagem a cada "Save" — ver handlers/settings.luau.
}

local shared_state: State = {
    api = nil,
    state_conn = nil,
    snap = nil,
    last_pairs = {},
    metrics_by_id = {},
    search_term = "",
    deploy_track = { service_id = "", deployment_id = "", running = false, name = "" },
    new_service_templates_all = {},
    jobs_by_id = {},
    jobs_inflight = {},
    docker_cleanup_last_run_at_raw = nil,
}

return shared_state