diff --git a/site/src/components/AppLink/AppLinkSkeleton.tsx b/site/src/components/AppLink/AppLinkSkeleton.tsx deleted file mode 100644 index 4da189b44e0b3..0000000000000 --- a/site/src/components/AppLink/AppLinkSkeleton.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { makeStyles } from "@material-ui/core/styles" -import { Skeleton } from "@material-ui/lab" -import { FC } from "react" -import { borderRadiusSm } from "theme/constants" - -export const AppLinkSkeleton: FC<{ width: number }> = ({ width }) => { - const styles = useStyles() - return ( - - ) -} - -const useStyles = makeStyles(() => ({ - skeleton: { - borderRadius: borderRadiusSm, - }, -})) diff --git a/site/src/components/GlobalSnackbar/utils.ts b/site/src/components/GlobalSnackbar/utils.ts index 5318453968fea..03daf29b888ee 100644 --- a/site/src/components/GlobalSnackbar/utils.ts +++ b/site/src/components/GlobalSnackbar/utils.ts @@ -64,14 +64,6 @@ function dispatchNotificationEvent( }) } -export const displayMsg = (msg: string, additionalMsg?: string): void => { - dispatchNotificationEvent( - MsgType.Info, - msg, - additionalMsg ? [additionalMsg] : undefined, - ) -} - export const displaySuccess = (msg: string, additionalMsg?: string): void => { dispatchNotificationEvent( MsgType.Success, diff --git a/site/src/components/RuntimeErrorState/RuntimeErrorReport.tsx b/site/src/components/RuntimeErrorState/RuntimeErrorReport.tsx deleted file mode 100644 index 55aba804998df..0000000000000 --- a/site/src/components/RuntimeErrorState/RuntimeErrorReport.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { makeStyles } from "@material-ui/core/styles" -import { ReactElement } from "react" -import { CodeBlock } from "../CodeBlock/CodeBlock" -import { createCtas } from "./createCtas" - -const Language = { - reportLoading: "Generating crash report...", -} - -interface ReportState { - error: Error - mappedStack: string[] | null -} - -interface StackTraceAvailableMsg { - type: "stackTraceAvailable" - stackTrace: string[] -} - -/** - * stackTraceUnavailable is a Msg describing a stack trace not being available - */ -export const stackTraceUnavailable = { - type: "stackTraceUnavailable", -} as const - -type ReportMessage = StackTraceAvailableMsg | typeof stackTraceUnavailable - -export const stackTraceAvailable = ( - stackTrace: string[], -): StackTraceAvailableMsg => { - return { - type: "stackTraceAvailable", - stackTrace, - } -} - -const setStackTrace = ( - model: ReportState, - mappedStack: string[], -): ReportState => { - return { - ...model, - mappedStack, - } -} - -export const reducer = ( - model: ReportState, - msg: ReportMessage, -): ReportState => { - switch (msg.type) { - case "stackTraceAvailable": - return setStackTrace(model, msg.stackTrace) - case "stackTraceUnavailable": - return setStackTrace(model, ["Unable to get stack trace"]) - } -} - -export const createFormattedStackTrace = ( - error: Error, - mappedStack: string[] | null, -): string[] => { - return [ - "======================= STACK TRACE ========================", - "", - error.message, - ...(mappedStack ? mappedStack : []), - "", - "============================================================", - ] -} - -/** - * A code block component that contains the error stack resulting from an error boundary trigger - */ -export const RuntimeErrorReport = ({ - error, - mappedStack, -}: ReportState): ReactElement => { - const styles = useStyles() - - if (!mappedStack) { - return ( - - ) - } - - const formattedStackTrace = createFormattedStackTrace(error, mappedStack) - return ( - - ) -} - -const useStyles = makeStyles(() => ({ - codeBlock: { - minHeight: "auto", - userSelect: "all", - width: "100%", - }, -})) diff --git a/site/src/components/RuntimeErrorState/createCtas.tsx b/site/src/components/RuntimeErrorState/createCtas.tsx deleted file mode 100644 index 5314441166892..0000000000000 --- a/site/src/components/RuntimeErrorState/createCtas.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import Button from "@material-ui/core/Button" -import { makeStyles } from "@material-ui/core/styles" -import RefreshIcon from "@material-ui/icons/Refresh" -import { ReactElement } from "react" -import { CopyButton } from "../CopyButton/CopyButton" - -export const Language = { - reloadApp: "Reload Application", - copyReport: "Copy Report", -} - -/** - * A wrapper component for a full-width copy button - */ -const CopyStackButton = ({ text }: { text: string }): ReactElement => { - const styles = useStyles() - - return ( - - ) -} - -/** - * A button that reloads our application - */ -const ReloadAppButton = (): ReactElement => { - const styles = useStyles() - - return ( - - ) -} - -/** - * createCtas generates an array of buttons to be used with our error boundary UI - */ -export const createCtas = (codeBlock: string[]): ReactElement[] => { - return [ - , - , - ] -} - -const useStyles = makeStyles((theme) => ({ - buttonWrapper: { - marginTop: theme.spacing(1), - marginLeft: 0, - flex: theme.spacing(1), - textTransform: "uppercase", - fontSize: theme.typography.fontSize, - }, - - copyButton: { - width: "100%", - marginRight: theme.spacing(1), - backgroundColor: theme.palette.primary.main, - textTransform: "uppercase", - fontSize: theme.typography.fontSize, - }, -})) diff --git a/site/src/components/TableCellLink/TableCellLink.tsx b/site/src/components/TableCellLink/TableCellLink.tsx deleted file mode 100644 index a371fddde7de4..0000000000000 --- a/site/src/components/TableCellLink/TableCellLink.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import Link from "@material-ui/core/Link" -import { makeStyles } from "@material-ui/core/styles" -import TableCell, { TableCellProps } from "@material-ui/core/TableCell" -import { Link as RouterLink } from "react-router-dom" -import { combineClasses } from "../../utils/combineClasses" - -// TableCellLink wraps a TableCell filling the entirety with a Link. -// This allows table rows to be clickable with browser-behavior like ctrl+click. -export const TableCellLink: React.FC< - React.PropsWithChildren< - TableCellProps & { - to: string - } - > -> = (props) => { - const styles = useStyles() - - return ( - - - {props.children} - - - ) -} - -const useStyles = makeStyles((theme) => ({ - cell: { - // This must override all padding for all rules on a TableCell. - // Otherwise, the link will not cover the entire region. - // It's unfortunate to use `!important`, but this seems to be - // a reasonable use-case. - padding: "0 !important", - }, - link: { - display: "block", - width: "100%", - border: "none", - background: "none", - paddingTop: theme.spacing(2), - paddingBottom: theme.spacing(2), - // This is required to hide all underlines for child elements! - textDecoration: "none !important", - }, -})) diff --git a/site/src/components/Tooltips/HelpTooltip/HelpTooltip.tsx b/site/src/components/Tooltips/HelpTooltip/HelpTooltip.tsx index 11167c062b06b..2ac2b0ef86b70 100644 --- a/site/src/components/Tooltips/HelpTooltip/HelpTooltip.tsx +++ b/site/src/components/Tooltips/HelpTooltip/HelpTooltip.tsx @@ -26,10 +26,6 @@ interface HelpTooltipProps { buttonClassName?: string } -export const Language = { - ariaLabel: "tooltip", -} - export const HelpTooltipContext = createContext< { open: boolean; onClose: () => void } | undefined >(undefined) @@ -108,7 +104,7 @@ export const HelpTooltip: FC> = ({ onMouseLeave={() => { setIsOpen(false) }} - aria-label={Language.ariaLabel} + aria-label="More info" > diff --git a/site/src/components/UsersTable/UsersTable.test.tsx b/site/src/components/UsersTable/UsersTable.test.tsx deleted file mode 100644 index 42f2683d449df..0000000000000 --- a/site/src/components/UsersTable/UsersTable.test.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { fireEvent, screen } from "@testing-library/react" -import { Language as TooltipLanguage } from "components/Tooltips/HelpTooltip/HelpTooltip" -import { Language as UserRoleLanguage } from "components/Tooltips/UserRoleHelpTooltip" -import { render } from "testHelpers/renderHelpers" -import { UsersTable } from "./UsersTable" - -describe("AuditPage", () => { - it("renders a page with a title and subtitle", async () => { - // When - render( - jest.fn()} - onDeleteUser={() => jest.fn()} - onListWorkspaces={() => jest.fn()} - onActivateUser={() => jest.fn()} - onResetUserPassword={() => jest.fn()} - onUpdateUserRoles={() => jest.fn()} - isNonInitialPage={false} - actorID="12345678-1234-1234-1234-123456789012" - />, - ) - - // Then - const tooltipIcon = await screen.findByRole("button", { - name: TooltipLanguage.ariaLabel, - }) - fireEvent.mouseOver(tooltipIcon) - expect(await screen.findByText(UserRoleLanguage.title)).toBeInTheDocument() - }) -}) diff --git a/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.tsx b/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.tsx index 47babcc06ade8..4c46a84369cb1 100644 --- a/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.tsx +++ b/site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.tsx @@ -1,4 +1,3 @@ -import { makeStyles } from "@material-ui/core/styles" import { Template, TemplateVersion, @@ -55,26 +54,3 @@ export const TemplateSummaryPageView: FC = ({ ) } - -export const useStyles = makeStyles((theme) => { - return { - markdownSection: { - background: theme.palette.background.paper, - border: `1px solid ${theme.palette.divider}`, - borderRadius: theme.shape.borderRadius, - }, - - readmeLabel: { - color: theme.palette.text.secondary, - fontWeight: 600, - padding: theme.spacing(2, 3), - borderBottom: `1px solid ${theme.palette.divider}`, - }, - - markdownWrapper: { - padding: theme.spacing(0, 3, 5), - maxWidth: 800, - margin: "auto", - }, - } -}) diff --git a/site/src/pages/TemplateSettingsPage/TemplateVariablesPage/TemplateVariablesForm.tsx b/site/src/pages/TemplateSettingsPage/TemplateVariablesPage/TemplateVariablesForm.tsx index 43e362840bea6..ea944f18e3283 100644 --- a/site/src/pages/TemplateSettingsPage/TemplateVariablesPage/TemplateVariablesForm.tsx +++ b/site/src/pages/TemplateSettingsPage/TemplateVariablesPage/TemplateVariablesForm.tsx @@ -20,8 +20,6 @@ import { TemplateVariableField, } from "components/TemplateVariableField/TemplateVariableField" -export const getValidationSchema = (): Yup.AnyObjectSchema => Yup.object() - export interface TemplateVariablesForm { templateVersion: TemplateVersion templateVariables: TemplateVersionVariable[] diff --git a/site/src/utils/duration.ts b/site/src/utils/duration.ts deleted file mode 100644 index b65be8722d0cf..0000000000000 --- a/site/src/utils/duration.ts +++ /dev/null @@ -1,13 +0,0 @@ -import dayjs from "dayjs" -import duration from "dayjs/plugin/duration" -import relativeTime from "dayjs/plugin/relativeTime" - -dayjs.extend(duration) -dayjs.extend(relativeTime) - -export const humanDuration = ( - time: number, - durationUnitType?: duration.DurationUnitType | undefined, -) => { - return dayjs.duration(time, durationUnitType).humanize() -} diff --git a/site/src/xServices/setup/setupXService.ts b/site/src/xServices/setup/setupXService.ts index e0ce5eecd8557..c5dd918679cf6 100644 --- a/site/src/xServices/setup/setupXService.ts +++ b/site/src/xServices/setup/setupXService.ts @@ -2,10 +2,6 @@ import * as API from "api/api" import * as TypesGen from "api/typesGenerated" import { assign, createMachine } from "xstate" -export const Language = { - createFirstUserError: "Failed to create the user.", -} - export interface SetupContext { error?: unknown firstUser?: TypesGen.CreateFirstUserRequest diff --git a/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts b/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts index 7c2bcced7a699..c3ff74fa76197 100644 --- a/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts +++ b/site/src/xServices/templateVersionEditor/templateVersionEditorXService.ts @@ -14,10 +14,6 @@ import { isAllowedFile } from "utils/templateVersion" import { TarReader, TarWriter } from "utils/tar" import { PublishVersionData } from "pages/TemplateVersionPage/TemplateVersionEditorPage/types" -export interface CreateVersionData { - file: File -} - export interface TemplateVersionEditorMachineContext { orgId: string templateId?: string diff --git a/site/src/xServices/workspaces/workspacesXService.ts b/site/src/xServices/workspaces/workspacesXService.ts deleted file mode 100644 index 7bf4157a0ce18..0000000000000 --- a/site/src/xServices/workspaces/workspacesXService.ts +++ /dev/null @@ -1,414 +0,0 @@ -import { getPaginationData } from "components/PaginationWidget/utils" -import { - PaginationContext, - paginationMachine, - PaginationMachineRef, -} from "xServices/pagination/paginationXService" -import { ActorRefFrom, assign, createMachine, spawn, send } from "xstate" -import * as API from "../../api/api" -import { getErrorMessage } from "../../api/errors" -import * as TypesGen from "../../api/typesGenerated" -import { - displayError, - displayMsg, - displaySuccess, -} from "../../components/GlobalSnackbar/utils" -import { queryToFilter } from "../../utils/filters" - -export const workspacePaginationId = "workspacePagination" - -/** - * Workspace item machine - * - * It is used to control the state and actions of each workspace in the - * workspaces page view - **/ -interface WorkspaceItemContext { - data: TypesGen.Workspace - updatedTemplate?: TypesGen.Template -} - -type WorkspaceItemEvent = - | { - type: "UPDATE_VERSION" - } - | { - type: "UPDATE_DATA" - data: TypesGen.Workspace - } - -export const workspaceItemMachine = createMachine( - { - id: "workspaceItemMachine", - predictableActionArguments: true, - tsTypes: {} as import("./workspacesXService.typegen").Typegen0, - schema: { - context: {} as WorkspaceItemContext, - events: {} as WorkspaceItemEvent, - services: {} as { - getTemplate: { - data: TypesGen.Template - } - startWorkspace: { - data: TypesGen.WorkspaceBuild - } - getWorkspace: { - data: TypesGen.Workspace - } - }, - }, - type: "parallel", - - states: { - updateVersion: { - initial: "idle", - states: { - idle: { - on: { - UPDATE_VERSION: { - target: "gettingUpdatedTemplate", - // We can improve the UI by optimistically updating the workspace status - // to "Pending" so the UI can display the updated state right away and we - // don't need to display an extra spinner. - actions: [ - "assignPendingStatus", - "displayUpdatingVersionMessage", - ], - }, - UPDATE_DATA: { - actions: "assignUpdatedData", - }, - }, - }, - gettingUpdatedTemplate: { - invoke: { - id: "getTemplate", - src: "getTemplate", - onDone: { - actions: "assignUpdatedTemplate", - target: "restartingWorkspace", - }, - onError: { - target: "idle", - actions: "displayUpdateVersionError", - }, - }, - }, - restartingWorkspace: { - invoke: { - id: "startWorkspace", - src: "startWorkspace", - onDone: { - actions: "assignLatestBuild", - target: "waitingToBeUpdated", - }, - onError: { - target: "idle", - actions: "displayUpdateVersionError", - }, - }, - }, - waitingToBeUpdated: { - after: { - 5000: "gettingUpdatedWorkspaceData", - }, - }, - gettingUpdatedWorkspaceData: { - invoke: { - id: "getWorkspace", - src: "getWorkspace", - onDone: [ - { - target: "waitingToBeUpdated", - cond: "isOutdated", - actions: ["assignUpdatedData"], - }, - { - target: "idle", - actions: [ - "assignUpdatedData", - "displayUpdatedSuccessMessage", - ], - }, - ], - }, - }, - }, - }, - }, - }, - { - guards: { - isOutdated: (_, event) => event.data.outdated, - }, - services: { - getTemplate: (context) => API.getTemplate(context.data.template_id), - startWorkspace: (context) => { - if (!context.updatedTemplate) { - throw new Error("Updated template is not loaded.") - } - - return API.startWorkspace( - context.data.id, - context.updatedTemplate.active_version_id, - ) - }, - getWorkspace: (context) => API.getWorkspace(context.data.id), - }, - actions: { - assignUpdatedTemplate: assign({ - updatedTemplate: (_, event) => event.data, - }), - assignLatestBuild: assign({ - data: (context, event) => { - return { - ...context.data, - latest_build: event.data, - } - }, - }), - displayUpdateVersionError: (_, event) => { - const message = getErrorMessage( - event.data, - "Error on update workspace version.", - ) - displayError(message) - }, - displayUpdatingVersionMessage: () => { - displayMsg("Updating workspace...") - }, - assignPendingStatus: assign({ - data: (ctx) => { - return { - ...ctx.data, - latest_build: { - ...ctx.data.latest_build, - status: "pending" as TypesGen.WorkspaceStatus, - job: { - ...ctx.data.latest_build.job, - status: "pending" as TypesGen.ProvisionerJobStatus, - }, - }, - } - }, - }), - displayUpdatedSuccessMessage: () => { - displaySuccess("Workspace updated successfully.") - }, - assignUpdatedData: assign({ - data: (_, event) => event.data, - }), - }, - }, -) - -/** - * Workspaces machine - * - * It is used to control the state of the workspace list - **/ - -export type WorkspaceItemMachineRef = ActorRefFrom - -interface WorkspacesContext { - workspaceRefs?: WorkspaceItemMachineRef[] - paginationRef?: PaginationMachineRef - filter: string - count?: number - getWorkspacesError?: Error | unknown - paginationContext: PaginationContext -} - -type WorkspacesEvent = - | { type: "UPDATE_PAGE"; page: string } - | { type: "UPDATE_VERSION"; workspaceId: string } - | { type: "UPDATE_FILTER"; query?: string } - -export const workspacesMachine = - /** @xstate-layout N4IgpgJg5mDOIC5QHcD2AnA1rADgQwGM4BlAFz1LADpZz1SBLAOygAU8pmKHUmBiANoAGALqJQOVLAaNe4kAA9EAWgAcANnVUAzAEYATOoCcAdiFHt+obtUAaEAE9Euo6qo2LR0y-3bV+1QBfQPs0LFxCEnJKKhhSRhYAdQxsfCJYPgheamYAN1RMajjk8LS4YTEkEElpWSZ5JQRlAFYjABYdA1UjXVbmtu1teycENqN9KhNm5tVVbXU5nvHm4NCUiPSyCiKweOYoEtTIjKymHKZ8wtjdw43y3UqJKRkeeqrG5X02rT11XSEAiYBl4TMNEG1+lQhEJ1FZtF4hM0TPpdKsQGEjptojs9kl1mUMmB0OgMFQcAAbCgAMwwAFtrqRbgSKvIai85O8VM11EIdNMNEJtBCBkNHODoVQAtChCZ4eYbNo0Ri7rAtjEAK44CDcPGlSIAJTAVJO2SoeQK1E12soTINRtgLKqbLqDRU+n0RioRn6JhMqiRQjaQn96jBCBszSozX0zW0rSMxnUst0ipC6PxxzV1GQeBkABVUIaqeg4AALW3pPgKWjbKh4KmUdAACma0oAlHxlQSs1Qc-nC0aS7Byxn0o6nrVXq6mtzPULevDdEm2kHQWKEB6Jt045ZoUn-uo2krR1FtnwAKqsAAiAEE8wBRAD6ADV7-riABJADyADlx9VnhdTkmksXRJgTBY-GMYNvTDZRek9TRej9XQDFcP0VjTLtM2xC9rzvJ9WBvABxe9-2dKdgOUWUtGjL5Az8QY-TsdcEyjVCTBsIEvB49Rjz1LEz0vW8H0fAAxD8ABkH31cjAMo0APn6Dpvhlf4E3+GNmjgpdtCoMYYVUAZuQ0JMgjRJhUAgOB5GwwSYhreh9nYTgmG4DkJ3ZN5FJULwOiFMw-SsVRrEFMNvS9Hiouikx+MxU8YjiBIDhPeAnXkjzFF8gYdERfwEy8PwUTDQw9KRZoU2hcZNDafRYqw1KeytHUUoEsAizSzygJ8poAjcKxvmMNpZW5VoSosL0iu6UrLGROKVR7PtSALIshxHNrOoAydMqUmYo00ExTCM-pBhK8woRTWFWh5WYgyPBqNqzVkMu8rKmn+WqdGGmV-GDULRRGLQotUXRfVBsx2kRYJgiAA */ - createMachine( - { - tsTypes: {} as import("./workspacesXService.typegen").Typegen1, - schema: { - context: {} as WorkspacesContext, - events: {} as WorkspacesEvent, - services: {} as { - getWorkspaces: { - data: TypesGen.WorkspacesResponse - } - updateWorkspaceRefs: { - data: { - refsToKeep: WorkspaceItemMachineRef[] - newWorkspaces: TypesGen.Workspace[] - } - } - }, - }, - predictableActionArguments: true, - id: "workspacesState", - on: { - UPDATE_VERSION: { - actions: "triggerUpdateVersion", - }, - UPDATE_PAGE: { - target: "gettingWorkspaces", - actions: "updateURL", - }, - UPDATE_FILTER: { - actions: ["assignFilter", "sendResetPage"], - }, - }, - initial: "startingPagination", - states: { - startingPagination: { - entry: "assignPaginationRef", - always: { - target: "gettingWorkspaces", - }, - }, - gettingWorkspaces: { - entry: "clearGetWorkspacesError", - invoke: { - src: "getWorkspaces", - id: "getWorkspaces", - onDone: [ - { - target: "waitToRefreshWorkspaces", - cond: "isEmpty", - actions: ["assignWorkspaceRefs", "assignCount"], - }, - { - target: "updatingWorkspaceRefs", - actions: "assignCount", - }, - ], - onError: [ - { - target: "waitToRefreshWorkspaces", - actions: "assignGetWorkspacesError", - }, - ], - }, - }, - updatingWorkspaceRefs: { - invoke: { - src: "updateWorkspaceRefs", - id: "updateWorkspaceRefs", - onDone: [ - { - target: "waitToRefreshWorkspaces", - actions: "assignUpdatedWorkspaceRefs", - }, - ], - }, - }, - waitToRefreshWorkspaces: { - after: { - "5000": { - target: "gettingWorkspaces", - actions: [], - internal: false, - }, - }, - }, - }, - }, - { - guards: { - isEmpty: (context) => !context.workspaceRefs, - }, - actions: { - assignWorkspaceRefs: assign({ - workspaceRefs: (_, event) => - event.data.workspaces.map((data) => { - return spawn(workspaceItemMachine.withContext({ data }), data.id) - }), - }), - assignCount: assign({ - count: (_, event) => event.data.count, - }), - assignPaginationRef: assign({ - paginationRef: (context) => - spawn( - paginationMachine.withContext(context.paginationContext), - workspacePaginationId, - ), - }), - assignFilter: assign({ - filter: (context, event) => event.query ?? context.filter, - }), - sendResetPage: send( - { type: "RESET_PAGE" }, - { to: workspacePaginationId }, - ), - assignGetWorkspacesError: assign({ - getWorkspacesError: (_, event) => event.data, - }), - clearGetWorkspacesError: (context) => - assign({ ...context, getWorkspacesError: undefined }), - triggerUpdateVersion: (context, event) => { - const workspaceRef = context.workspaceRefs?.find( - (ref) => ref.id === event.workspaceId, - ) - - if (!workspaceRef) { - throw new Error(`No workspace ref found for ${event.workspaceId}.`) - } - - workspaceRef.send("UPDATE_VERSION") - }, - assignUpdatedWorkspaceRefs: assign({ - workspaceRefs: (_, event) => { - const newWorkspaceRefs = event.data.newWorkspaces.map((workspace) => - spawn( - workspaceItemMachine.withContext({ data: workspace }), - workspace.id, - ), - ) - return event.data.refsToKeep.concat(newWorkspaceRefs) - }, - }), - }, - services: { - getWorkspaces: (context) => { - if (context.paginationRef) { - const { offset, limit } = getPaginationData(context.paginationRef) - return API.getWorkspaces({ - ...queryToFilter(context.filter), - offset, - limit, - }) - } else { - throw new Error("Cannot get workspaces without pagination data") - } - }, - updateWorkspaceRefs: (context, event) => { - const refsToKeep: WorkspaceItemMachineRef[] = [] - context.workspaceRefs?.forEach((ref) => { - const matchingWorkspace = event.data.workspaces.find( - (workspace) => ref.id === workspace.id, - ) - if (matchingWorkspace) { - // if a workspace machine reference describes a workspace that has not been deleted, - // update its data and mark it as a refToKeep - ref.send({ type: "UPDATE_DATA", data: matchingWorkspace }) - refsToKeep.push(ref) - } else { - // if it describes a workspace that has been deleted, stop the machine - ref.stop && ref.stop() - } - }) - - const newWorkspaces = event.data.workspaces.filter( - (workspace) => - !context.workspaceRefs?.find((ref) => ref.id === workspace.id), - ) - - return Promise.resolve({ - refsToKeep, - newWorkspaces, - }) - }, - }, - }, - )