Fix for SSE error when trying to send when the controller has been aborted
This commit is contained in:
@@ -44,25 +44,23 @@ import {
|
||||
v3ApiKeysPath,
|
||||
v3BatchesPath,
|
||||
v3BillingPath,
|
||||
v3ConcurrencyPath,
|
||||
v3DeploymentsPath,
|
||||
v3EnvironmentPath,
|
||||
v3EnvironmentVariablesPath,
|
||||
v3ProjectAlertsPath,
|
||||
v3ProjectPath,
|
||||
v3ProjectSettingsPath,
|
||||
v3QueuesPath,
|
||||
v3RunsPath,
|
||||
v3SchedulesPath,
|
||||
v3TestPath,
|
||||
v3UsagePath,
|
||||
} from "~/utils/pathBuilder";
|
||||
import { useDevPresence } from "../DevPresence";
|
||||
import { ImpersonationBanner } from "../ImpersonationBanner";
|
||||
import { PackageManagerProvider, TriggerDevStepV3 } from "../SetupCommands";
|
||||
import { UserProfilePhoto } from "../UserProfilePhoto";
|
||||
import connectedImage from "../../assets/images/cli-connected.png";
|
||||
import disconnectedImage from "../../assets/images/cli-disconnected.png";
|
||||
import { FreePlanUsage } from "../billing/FreePlanUsage";
|
||||
import { useDevPresence } from "../DevPresence";
|
||||
import { ImpersonationBanner } from "../ImpersonationBanner";
|
||||
import { Button, ButtonContent, LinkButton } from "../primitives/Buttons";
|
||||
import {
|
||||
Dialog,
|
||||
@@ -80,18 +78,14 @@ import {
|
||||
PopoverTrigger,
|
||||
} from "../primitives/Popover";
|
||||
import { TextLink } from "../primitives/TextLink";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../primitives/Tooltip";
|
||||
import { PackageManagerProvider, TriggerDevStepV3 } from "../SetupCommands";
|
||||
import { UserProfilePhoto } from "../UserProfilePhoto";
|
||||
import { EnvironmentSelector } from "./EnvironmentSelector";
|
||||
import { HelpAndFeedback } from "./HelpAndFeedbackPopover";
|
||||
import { SideMenuHeader } from "./SideMenuHeader";
|
||||
import { SideMenuItem } from "./SideMenuItem";
|
||||
import { SideMenuSection } from "./SideMenuSection";
|
||||
import {
|
||||
SimpleTooltip,
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "../primitives/Tooltip";
|
||||
|
||||
type SideMenuUser = Pick<User, "email" | "admin"> & { isImpersonating: boolean };
|
||||
export type SideMenuProject = Pick<
|
||||
@@ -194,6 +188,13 @@ export function SideMenu({
|
||||
to={v3SchedulesPath(organization, project, environment)}
|
||||
data-action="schedules"
|
||||
/>
|
||||
<SideMenuItem
|
||||
name="Queues"
|
||||
icon={RectangleStackIcon}
|
||||
activeIconColor="text-blue-500"
|
||||
to={v3QueuesPath(organization, project, environment)}
|
||||
data-action="queues"
|
||||
/>
|
||||
<SideMenuItem
|
||||
name="Deployments"
|
||||
icon={ServerStackIcon}
|
||||
@@ -241,14 +242,6 @@ export function SideMenu({
|
||||
to={v3EnvironmentVariablesPath(organization, project, environment)}
|
||||
data-action="environment variables"
|
||||
/>
|
||||
|
||||
<SideMenuItem
|
||||
name="Concurrency limits"
|
||||
icon={RectangleStackIcon}
|
||||
activeIconColor="text-indigo-500"
|
||||
to={v3ConcurrencyPath(organization, project, environment)}
|
||||
data-action="concurrency"
|
||||
/>
|
||||
<SideMenuItem
|
||||
name="Project settings"
|
||||
icon={Cog8ToothIcon}
|
||||
|
||||
@@ -53,6 +53,26 @@ export function createSSELoader(options: SSEOptions) {
|
||||
);
|
||||
};
|
||||
|
||||
const createSafeSend = (originalSend: SendFunction): SendFunction => {
|
||||
return (event) => {
|
||||
try {
|
||||
if (!internalController.signal.aborted) {
|
||||
originalSend(event);
|
||||
}
|
||||
// If controller is aborted, silently ignore the send attempt
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
if (error.message?.includes("Controller is already closed")) {
|
||||
// Silently handle controller closed errors
|
||||
return;
|
||||
}
|
||||
log(`Error sending event: ${error.message}`);
|
||||
}
|
||||
throw error; // Re-throw other errors
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const context: SSEContext = {
|
||||
id,
|
||||
request,
|
||||
@@ -115,12 +135,13 @@ export function createSSELoader(options: SSEOptions) {
|
||||
|
||||
return eventStream(combinedSignal, function setup(send) {
|
||||
connections.add(id);
|
||||
const safeSend = createSafeSend(send);
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
log("Initializing");
|
||||
if (handlers.initStream) {
|
||||
const shouldContinue = await handlers.initStream({ send });
|
||||
const shouldContinue = await handlers.initStream({ send: safeSend });
|
||||
if (shouldContinue === false) {
|
||||
log("initStream returned false, so we'll stop the stream");
|
||||
internalController.abort("Init requested stop");
|
||||
@@ -138,7 +159,7 @@ export function createSSELoader(options: SSEOptions) {
|
||||
|
||||
if (handlers.iterator) {
|
||||
try {
|
||||
const shouldContinue = await handlers.iterator({ date, send });
|
||||
const shouldContinue = await handlers.iterator({ date, send: safeSend });
|
||||
if (shouldContinue === false) {
|
||||
log("iterator return false, so we'll stop the stream");
|
||||
internalController.abort("Iterator requested stop");
|
||||
@@ -173,7 +194,7 @@ export function createSSELoader(options: SSEOptions) {
|
||||
log("Cleanup called");
|
||||
if (handlers.cleanup) {
|
||||
try {
|
||||
handlers.cleanup({ send });
|
||||
handlers.cleanup({ send: safeSend });
|
||||
} catch (error) {
|
||||
log(
|
||||
`Error in cleanup handler: ${
|
||||
|
||||
Reference in New Issue
Block a user