> ## Documentation Index
> Fetch the complete documentation index at: https://docs.squarecloud.app/llms.txt
> Use this file to discover all available pages before exploring further.

# SDK JavaScript: migrar da v5 para a v6

> Atualize o @squarecloud/api da v5 para a v6: dados simples em vez de classes, ids como primeiro argumento, uma classe de erro, timeouts e novas tentativas.

A v6 é uma reescrita: um cliente plano, dados simples em vez de classes, ids como primeiro argumento e uma única classe de erro. A maioria das mudanças é mecânica.

## Visão geral

| v5 | v6 |
| - | - |
| Classes com métodos (`app.start()`) | Dados simples; métodos no cliente (`api.apps.start(appId)`) |
| `Collection` | `Array` |
| Campos de classe em camelCase com `Date`s (`createdAt`, `modifiedAt`, `uptime`) | Os próprios campos e valores da API (`created_at` e `modified` como strings ISO, `uptime` em ms) |
| `Buffer` | `Uint8Array` (um `Buffer` ainda é aceito como entrada) |
| Mutações retornam `Promise<boolean>` (sempre `true`) | `Promise<void>`, ou os dados que a API retorna (`envs.*` → `EnvVars`, `deploys.setWebhook` → URL, `deploys.linkGithubApp` → `LinkedRepository`, `resetCredentials` → a senha) |
| `SquareCloudAPIError extends TypeError` apenas com `code` | `extends Error` com `status`, `code`, `message` (a do servidor), `method`, `path`, `cause` |
| Eventos (`userUpdate`, `statusUpdate`, `logsUpdate`, `snapshotsUpdate`) e `api.cache`/`app.cache` | Removidos: mantenha seu próprio estado |
| `api.api.request()` (o `APIService` bruto) | Removido: toda operação tem um método |
| Sem timeout, sem novas tentativas | Timeout de 30 s por tentativa, novas tentativas apenas para falhas seguras (veja [Mudanças de comportamento](#mudanças-de-comportamento)) |
| Node.js >= 20 | Node.js >= 22, Deno, Bun, edge |
| `@squarecloud/api-types` | Os tipos vêm com o SDK (`import type { App } from "@squarecloud/api"`) |

## Construção e opções

| v5 | v6 | Observações |
| - | - | - |
| `new SquareCloudAPI(key)` | `new SquareCloudAPI(key, { baseUrl?, timeoutMs?, maxRetries?, userAgent?, fetch? })` | Uma chave vazia ou composta apenas de espaços lança um `TypeError`. |
| `SquareCloudAPI.apiInfo` | `BASE_URL` e `{ baseUrl }` | `BASE_URL` = `https://api.squarecloud.app/v2`. |
| (nenhum) | `timeoutMs` (30000) | Por tentativa; chamadas mantidas abertas e `ai.chat` esperam pelo menos 120 s. `<= 0`, `Infinity` ou `>= 2^31` desativa todos os timeouts, incluindo os mínimos. |
| (nenhum) | `maxRetries` (2) | Apenas erros de rede em GET e os códigos 503 listados; nunca 429. |
| (nenhum) | `userAgent` | Substitui o header inteiro (padrão `squarecloud-sdk-js/<version>`). |
| (nenhum) | `fetch` | `fetch` personalizado (proxies, tracing, testes). |

## Método a método

| v5 | v6 | Observações |
| - | - | - |
| `api.user.get()` → `User` | `api.account.me()` | Retorna `{ user, applications, databases }`. |
| `user.plan.expiresIn` | `new Date(user.plan.duration)` | Um timestamp; `null` nunca expira. |
| `api.user.snapshots(scope)` | `api.account.snapshots({ scope })` | |
| `api.service.status()` | `api.service.status()` | Novo formato: `status`, `message`, `services`, `dependencies`... |
| `api.applications.get()` | `(await api.account.me()).applications` | |
| `api.applications.get(id)` / `fetch(id)` / `app.fetch()` | `api.apps.get(id)` | Também encontra aplicações compartilhadas em workspaces. |
| `app.isWebsite()` | `Boolean((await api.apps.get(id)).domain)` | |
| `api.applications.create(file)` | `api.apps.create(file, { signal })` | Um caminho, `Blob`/`File` ou `Uint8Array`; sem timeout. |
| `api.applications.statusAll()` | `api.apps.statusAll({ workspaceId? })` | Objetos simples `{ id, running, cpu?, ram? }` (a v5 aninhava `cpu`/`ram` em `usage`). |
| `statusAll()[i].fetch()` (`SimpleApplicationStatus`, `SimpleDatabaseStatus`) | `api.apps.status(id)` / `api.databases.status(id)` | |
| `api.applications.domains()` / `loadBalancers()` | `api.apps.domains()` / `api.apps.loadBalancers()` | |
| `app.getStatus()` | `api.apps.status(id, { raw? })` | `cpu`, `ram`, `storage` e `network` ficam no nível superior (a v5 os aninhava em `usage`); `uptime` é o timestamp de início em ms (`null` quando parada), não um `Date`. |
| `app.getLogs()` | `api.apps.logs(id)` | |
| `app.getMetrics()` | `api.apps.metrics(id)` | Ponto mais recente primeiro. |
| `app.realtime()` → `Response` bruto | `api.apps.realtime(id, { signal })` | Um async iterable de eventos já interpretados. |
| `app.start()` / `stop()` / `restart()` / `delete()` | `api.apps.start(id)` / `stop(id)` / `restart(id)` / `delete(id)` | |
| `app.commit(file, fileName)` | `api.apps.commit(id, file, { path?, filename?, signal? })` | |
| `app.files.list(path)` | `api.apps.files.list(id, path?)` | Um diretório inexistente lança 404 `FILE_NOT_FOUND` (antes era uma lista vazia). |
| `app.files.read(path)` → `Buffer \| undefined` | `api.apps.files.read(id, path)` → `Uint8Array` | Solicitado como base64 e decodificado (a v5 lia um array de bytes). Bytes vazios, nunca `undefined`, quando a API não envia conteúdo. |
| `app.files.create(file, fileName, dir)` | ``api.apps.files.write(id, `${dir}/${fileName}`, content)`` | Uma string é o **conteúdo** (enviado como texto simples); a v5 a lia como um caminho local. Bytes são enviados como base64. Conteúdo vazio cria um arquivo vazio. |
| `app.files.edit(file, path)` | `api.apps.files.write(id, path, content)` | Mesmo formato de envio do `write`: texto para strings, base64 para bytes. |
| `app.files.move(path, newPath)` / `delete(path)` | `api.apps.files.move(id, path, to)` / `delete(id, path)` | |
| `app.envs.list()` | `api.apps.envs.get(id)` | |
| `app.envs.set(envs)` / `replace(envs)` / `delete(keys)` | `api.apps.envs.set(id, envs)` / `replace(id, envs)` / `delete(id, keys)` | Cada um retorna as variáveis resultantes. |
| `app.snapshots.list()` | `api.apps.snapshots.list(id)` | Os itens ganham `name`, `runtime`, `origin`, `version_id` e uma `url` de download assinada, tudo como a API envia. |
| `app.snapshots.create()` | `api.apps.snapshots.create(id)` | `{ pending: true }` em um 202 (a v5 lançava erro), senão `{ pending: false, url, key }`. |
| `app.snapshots.download()` → `Buffer` | `const s = await api.apps.snapshots.create(id)`, depois `if (!s.pending) await api.downloadSnapshot(s.url)` | Um `ReadableStream`, nada armazenado em memória. Um snapshot pendente (202) ainda não tem URL: a v5 lançava erro. |
| `snapshot.url` / `snapshot.download()` | `snapshot.url` / `api.downloadSnapshot(snapshot.url)` | A própria URL assinada da API (a v5 montava uma URL errada a partir do id de quem chamava). |
| `app.snapshots.restore({ snapshotId, versionId })` | `api.apps.snapshots.restore(id, name, versionId)` | Passe o `name` e o `version_id` de um snapshot listado. Veja [Snapshots](/pt-br/sdks/js/snapshots#restaurando-um-snapshot) para os erros. |
| `app.deploys.integrateGithubWebhook(token)` | `api.apps.deploys.setWebhook(id, token)` | Retorna a URL do webhook (`""` quando removido com `"@"`). |
| `app.deploys.linkGithubApp({ repositoryName, repositoryBranch })` | `api.apps.deploys.linkGithubApp(id, repository, branch)` | Posicional. Retorna `{ id, full_name, branch }`. Chaves de API agora são aceitas (escopo `apps:deploy`); a v5 precisava de um JWT de sessão. |
| `app.deploys.unlinkGithubApp()` → `boolean` | `api.apps.deploys.unlinkGithubApp(id)` | Resolve para `void` (era `boolean`). `400 GIT_NOT_CONFIGURED` quando nada está vinculado. |
| `app.deploys.list()` → `Deployment[][]` | `api.apps.deploys.list(id)` → `DeployEvent[][]` | Um deploy que falhou termina em `state: "error"` com `code` (e `message` quando há detalhes); `source` é sempre `"git"`. |
| `app.deploys.current()` | `api.apps.deploys.current(id)` → `DeployCurrent` | `{}` quando nada está configurado. |
| `app.deploys.webhookURL()` | `(await api.apps.deploys.current(id)).webhook` | |
| `app.network` (apenas em `WebsiteApplication`) | `api.apps.network` | Qualquer id de aplicação; a API rejeita aplicações que não são web. |
| `network.setCustomDomain(domain)` | `api.apps.network.setDomain(id, domain)` | |
| `network.analytics({ start, end, contentType, ... })` | `api.apps.network.analytics(id, start, end, { content_type, ... })` | `null` para uma janela vazia. |
| `network.errors({ start, end, include4xx })` | `api.apps.network.errors(id, start, end, { include_4xx })` | `null` para uma janela vazia. |
| `network.logs({ start, end })` / `performance({ start, end })` | `api.apps.network.logs(id, start, end)` / `performance(id, start, end)` | |
| `network.dns()` / `purgeCache()` | `api.apps.network.dns(id)` / `purgeCache(id)` | |
| `api.databases.fetch(id)` / `db.fetch()` | `api.databases.get(id)` | |
| `api.databases.create(options)` / `statusAll()` | inalterado | `statusAll()` retorna objetos simples `{ id, running, cpu?, ram? }`. |
| `db.getStatus()` / `getMetrics()` | `api.databases.status(id, { raw? })` / `metrics(id)` | `ram` é a RAM em uso, por exemplo `"120.4MB"` (um número com `raw`). As métricas vêm do ponto mais recente para o mais antigo. |
| `db.start()` / `stop()` / `update(changes)` / `delete()` | `api.databases.start(id)` / `stop(id)` / `update(id, changes)` / `delete(id)` | |
| `db.credentials.certificate()` | `api.databases.certificate(id)` | |
| `db.credentials.reset(type)` | `api.databases.resetCredentials(id, type)` | A nova senha (`""` para `"certificate"`). |
| `db.snapshots.list()` / `create()` / `download()` | `api.databases.snapshots.list(id)` / `create(id)` / `api.downloadSnapshot(url)` | `download()` como nas aplicações: `create(id)`, depois `downloadSnapshot(s.url)` quando não estiver pendente. |
| `db.snapshots.restore(snapshotId, versionId)` | `api.databases.snapshots.restore(id, name, versionId)` | Passe o `name` e o `version_id` de um snapshot listado. |
| `api.workspaces.list()` / `fetch(id)` / `workspace.fetch()` | `api.workspaces.list()` / `get(id)` | |
| `api.workspaces.create({ name })` | `api.workspaces.create(name)` | Retorna `WorkspaceCreated` (`{ id, name }`). |
| `api.workspaces.delete(id)` / `workspace.delete()` | `api.workspaces.delete(id)` | |
| `api.workspaces.leave(id)` / `workspace.leave()` | `api.workspaces.leave(id)` | |
| `api.workspaces.generateInviteCode()` | `api.workspaces.members.inviteCode()` | |
| `workspace.members.add(code, group)` | `api.workspaces.members.add(workspaceId, code, group)` | |
| `workspace.members.update(memberId, group)` / `remove(memberId)` | `api.workspaces.members.update(workspaceId, memberId, group)` / `remove(workspaceId, memberId)` | |
| `workspace.applications.add(appId)` / `remove(appId)` | `api.workspaces.apps.add(workspaceId, appId)` / `remove(workspaceId, appId)` | |
| (nenhum) | `api.ai.chat(request)`, `api.downloadSnapshot(url)`, `BASE_URL` | Novo. |

## Tipos

Os tipos vêm com o SDK e espelham os nomes de campos da API. As principais renomeações em relação ao `@squarecloud/api-types` e às classes da v5:

| v5 | v6 |
| - | - |
| `Application`, `WebsiteApplication`, `BaseApplication` | `App` (`api.apps.get()`), `AppSummary` (`account.me()`), `AppCreated` |
| `ApplicationStatus`, `SimpleApplicationStatus`, `SimpleDatabaseStatus` | `RuntimeStats` (`RuntimeStats<true>` com `{ raw: true }`), `StatusListItem` |
| `User` | `Account` (`{ user, applications, databases }`), `User`, `Plan` |
| `Snapshot`, `DatabaseSnapshot` | `Snapshot`, `SnapshotCreated` |
| `Deployment`, `DeploymentState` | `DeployEvent`, `DeployCurrent` (com `DeployRepository`), `LinkedRepository` |
| Objetos de consulta de rede | `AnalyticsFilters` (os filtros opcionais; `start` e `end` são argumentos) |
| `Workspace` | `Workspace`, `WorkspaceCreated`, `WorkspaceGroup` |
| `APIErrorCode` (objeto em runtime) | `ErrorCode` (apenas tipo, sem custo em runtime; todos os códigos do contrato da API) |
| `APIEndpoint`, `APIEndpoints`, `APIMethod`, `APIRequestArgs`, `APIRequestOptions`, `APIResponse`, `QueryOrBody`, `ClientEvents`, `TypedEventEmitter`, `CollectionConstructor` | Removidos (infraestrutura de requisições, eventos e `Collection`) |

## Erros

```ts theme={"system"}
// v5
catch (e) { if (e.code === "RATE_LIMIT_EXCEEDED") ... }

// v6
catch (e) {
  if (e instanceof SquareCloudAPIError && e.status === 429) {
    console.log(e.code, e.message); // KEEP_CALM "Please wait 5 seconds..." or RATE_LIMITED
  }
}
```

* Os códigos sintéticos foram removidos (`RATE_LIMIT_EXCEEDED`, `PAYLOAD_TOO_LARGE`, `SERVER_UNAVAILABLE`, `UNKNOWN_ERROR_<status>`): o código real da API aparece (`RATE_LIMITED`, `KEEP_CALM`, `DAILY_SNAPSHOTS_LIMIT_REACHED`, `FILE_TOO_LARGE`...).
* Sem resposta: `status: 0` com `NETWORK_ERROR` (a causa em `cause`) ou `TIMEOUT`. Um corpo sem código resulta em `UNKNOWN_ERROR` com o `status` real e a mensagem `HTTP <status>`.
* `instanceof TypeError` não é mais verdadeiro para erros da API.
* Uma chave expirada resulta em 401 `ACCESS_DENIED`, assim como uma desconhecida.
* Os erros de `ai.chat()`, incluindo os de autenticação e rate limit, trazem o código da OpenAI em minúsculas (`access_denied`, `rate_limit_exceeded`, ...).
* Um `start`/`stop`/`restart` recusado resulta em 409 apenas com um código: `CONTAINER_ALREADY_STARTED`, `CONTAINER_ALREADY_STOPPED`, `CONTAINER_TEMPORARILY_SUSPENDED`, `CONTAINER_NOT_FOUND`, `CONTAINER_INSUFFICIENT_DISK_SPACE`, `CONTAINER_NETWORK_CONFLICT` ou `ACTION_FAILED`.

## Mudanças de comportamento

* As chamadas expiram: 30 s por tentativa por padrão (a v5 não tinha timeout), pelo menos 120 s para chamadas que o servidor mantém abertas. Defina `timeoutMs: 0` para nenhum.
* `files.write()` trata uma string como o conteúdo e a envia como texto simples; bytes vão como base64, seguros para binários, e conteúdo vazio cria um arquivo vazio (mesmo formato de envio dos SDKs Python e Go). `files.read()` solicita base64 e o decodifica.
* `files.list()` de um diretório inexistente lança 404 `FILE_NOT_FOUND` em vez de retornar `[]`.
* `snapshots.create()` retorna `{ pending: true }` em um 202 em vez de lançar erro.
* Resultados string nunca são `undefined`: `setWebhook` e `resetCredentials("certificate")` retornam `""` quando a API não envia nada; `deploys.current()` retorna `{}`.
* `realtime()` se reconecta em conexões perdidas e em `REALTIME_RECONNECT` (até 3 vezes seguidas, no máximo uma abertura a cada 5,5 s).
* Novas tentativas: erros de rede em GET e 503 `UPLOAD_BUSY`/`ANALYTICS_BUSY` (além de `DATABASE_UNAVAILABLE` em GET), com backoff. 429 nunca é repetido. `DATABASE_UNAVAILABLE` pode chegar depois que uma mutação foi aplicada: repita você mesmo suas mutações idempotentes.
* Ids vazios, `.` e `..` falham localmente com `INVALID_ID`.
* Valores de query que são `undefined`, `""` ou `false` não são enviados.

## Próximos passos

<CardGroup cols={2}>
  <Card title="Cliente" icon="plug" href="/pt-br/sdks/js/client">
    Instale o SDK e crie um cliente.
  </Card>

  <Card title="Erros" icon="triangle-exclamation" href="/pt-br/sdks/js/errors">
    Classe de erro, novas tentativas e rate limits.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.