Files
supabase/apps/studio/data/workers/workers.utils.ts
Jordi Enric 6c6ac567b1 feat(studio): workers list behind the workers flag (FE-4188) (#49193)
## What

The Workers list page at `/project/[ref]/workers`, behind
`useFlag('workers')`. Reads `GET /v2/projects/{ref}/workers`.

- Sidebar and command-menu entries, both hidden when the flag is off
- Name search, state and access filters, pagination
- Read-only

Gating, in order: flag off redirects to the project home; a 404 from the
API means the project is outside the alpha allow-list ("not enabled for
this project"); a 403 means the caller lacks the permission
(`NoPermission`); anything else is an `AlertError`.

`parseWorker` in `data/workers/workers.utils.ts` is the only place the
API shape becomes the view model. It validates with zod, so a drifted
response fails the query instead of half-rendering a row.

## How to test

Only on the **Mockamaster** project in staging — it is the one project
in the alpha allow-list, and standing a worker up anywhere else is
involved right now.

1. Staging dashboard → Mockamaster → **Compute** in the sidebar
2. Expect the `dashboard-test` worker: state `Active`, runtime Deno,
private, US West, 2 GB · 1 vCPU · 1 inst
3. Open any other project's `/workers` URL → "Compute is not enabled for
this project"
4. Turn the `workers` flag off → the sidebar entry disappears and the
URL redirects to the project home

Closes FE-4188
2026-08-20 17:54:57 +02:00

48 lines
1.4 KiB
TypeScript

import { z } from 'zod'
import type { Worker, WorkerBuildState } from '@/components/interfaces/Workers/Workers.types'
const BUILD_STATES = ['building', 'active', 'failed'] as const satisfies readonly WorkerBuildState[]
const WorkerResponseSchema = z.object({
id: z.string(),
attributes: z.object({
build_state: z.enum(BUILD_STATES).catch('failed'),
deleting: z.boolean().optional(),
image_version: z.string().optional(),
instances: z
.object({
declared: z.number(),
live: z.number(),
ready: z.number(),
stale: z.number(),
})
.optional(),
instances_error: z.string().optional(),
spec: z.object({
exposure: z.string(),
instances: z.number(),
runtime: z.string().optional(),
size: z.string(),
}),
state_reason: z.string().optional(),
}),
})
export const parseWorker = (datum: unknown): Worker => {
const { id, attributes } = WorkerResponseSchema.parse(datum)
return {
name: id,
buildState: attributes.build_state,
isDeleting: attributes.deleting ?? false,
runtime: attributes.spec.runtime,
size: attributes.spec.size,
access: attributes.spec.exposure === 'public' ? 'public' : 'private',
declaredInstances: attributes.spec.instances,
instances: attributes.instances,
imageVersion: attributes.image_version,
stateReason: attributes.state_reason,
instancesError: attributes.instances_error,
}
}