11import { Request , Response , NextFunction , Express } from 'express' ;
22import { AbortError } from 'saborter/errors' ;
33import { setSignalInExpressRequest } from './express.utils' ;
4+ import * as Constants from './express.constants' ;
45import * as Shared from '../../../shared' ;
56
67/**
@@ -76,7 +77,7 @@ class ExpressRequestInterruptionService {
7677 setSignalInExpressRequest ( req , controller ) ;
7778
7879 this . registerAbortableFunction ( requestId , ( ) => {
79- controller . abort ( new AbortError ( 'The endpoint was interrupted' , { initiator : 'server' } ) ) ;
80+ controller . abort ( new AbortError ( Constants . ENDPOINT_WAS_INTERRUPTED_MESSAGE , { initiator : 'server' } ) ) ;
8081 } ) ;
8182
8283 req . on ( 'close' , ( ) => {
@@ -85,6 +86,7 @@ class ExpressRequestInterruptionService {
8586 }
8687 } ) ;
8788
89+ // Triggers clearing of request id when request completes or fails, excluding the case of abort.
8890 res . on ( 'finish' , ( ) => {
8991 if ( ! controller . signal . aborted ) {
9092 this . abortRegistries . delete ( requestId ) ;
@@ -100,65 +102,14 @@ class ExpressRequestInterruptionService {
100102 *
101103 * This function:
102104 * - Adds a middleware that attaches an `AbortSignal` to every request.
103- * - Adds a POST endpoint (default `/api/cancel`) that can be called to abort a
104- * specific request by sending its request ID in the request body.
105105 *
106106 * @param app - Express application instance.
107- * @param options - Configuration options.
108- * @param options.endpointName - The path where the abort endpoint will be mounted.
109- * Defaults to `/api/cancel`.
107+
110108 * @returns An `ExpressRequestInterruptionService` instance, which can be used
111109 * to manually abort requests if needed.
112110 */
113- export const initRequestInterrupts = (
114- app : Express ,
115- { endpointName = '/api/cancel' } : { endpointName ?: string } = { }
116- ) => {
111+ export const initRequestInterrupts = ( app : Express ) : void => {
117112 const requestInterruptionService = new ExpressRequestInterruptionService ( ) ;
118113
119114 app . use ( requestInterruptionService . expressMiddleware ) ;
120-
121- app . post ( `${ endpointName } ` , async ( req , res ) => {
122- const requestId = await new Promise < string | undefined > ( ( resolve ) => {
123- let rawBody = '' ;
124-
125- req . on ( 'data' , ( chunk ) => {
126- rawBody += chunk ;
127- } ) ;
128- req . on ( 'end' , ( ) => {
129- resolve ( rawBody ) ;
130- } ) ;
131- } ) ;
132-
133- if ( requestId && requestInterruptionService . abort ( requestId ) ) {
134- res . status ( 200 ) . json ( { aborted : true } ) ;
135- } else {
136- res . status ( 404 ) . json ( { error : 'Request not found' } ) ;
137- }
138- } ) ;
139115} ;
140-
141- /**
142- * Retrieves the `signal` property from an Express Request object.
143- *
144- * @param {import('express').Request } req - The Express request object.
145- * @returns {AbortSignal | undefined } The abort signal attached to the request,
146- * or `undefined` if the property does not exist.
147- *
148- * @example
149- * // In a route handler
150- * app.get('/data', (req, res) => {
151- * const signal = getAbortSignal(req);
152- * fetch('https://api.example.com/data', { signal })
153- * .then(response => response.json())
154- * .then(data => res.json(data))
155- * .catch(err => {
156- * if (err.name === 'AbortError') {
157- * res.status(499).end();
158- * } else {
159- * res.status(500).end();
160- * }
161- * });
162- * });
163- */
164- export const getAbortSignal = ( req : Request ) : AbortSignal | undefined => ( req as any ) . signal ;
0 commit comments