@jaypie/express 1.2.6 → 1.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,5 +5,15 @@ export interface CorsConfig {
5
5
  }
6
6
  type CorsCallback = (err: Error | null, allow?: boolean) => void;
7
7
  export declare const dynamicOriginCallbackHandler: (origin?: string | string[]) => ((requestOrigin: string | undefined, callback: CorsCallback) => void);
8
+ /**
9
+ * CORS middleware with Lambda streaming support.
10
+ *
11
+ * For OPTIONS preflight requests, this middleware handles them early and
12
+ * terminates the response immediately. This is critical for Lambda streaming
13
+ * handlers where the response stream would otherwise stay open waiting for
14
+ * streaming data that never comes.
15
+ *
16
+ * For regular requests, delegates to the standard cors package behavior.
17
+ */
8
18
  declare const _default: (config?: CorsConfig) => ((req: Request, res: Response, next: NextFunction) => void);
9
19
  export default _default;
@@ -1222,9 +1222,90 @@ const corsHelper = (config = {}) => {
1222
1222
  };
1223
1223
  return expressCors(options);
1224
1224
  };
1225
+ //
1226
+ //
1227
+ // Constants
1228
+ //
1229
+ const HTTP_CODE_NO_CONTENT = 204;
1230
+ const HTTP_METHOD_OPTIONS = "OPTIONS";
1231
+ /**
1232
+ * CORS middleware with Lambda streaming support.
1233
+ *
1234
+ * For OPTIONS preflight requests, this middleware handles them early and
1235
+ * terminates the response immediately. This is critical for Lambda streaming
1236
+ * handlers where the response stream would otherwise stay open waiting for
1237
+ * streaming data that never comes.
1238
+ *
1239
+ * For regular requests, delegates to the standard cors package behavior.
1240
+ */
1225
1241
  var cors_helper = (config) => {
1226
1242
  const cors = corsHelper(config);
1243
+ const { origin, overrides = {} } = config || {};
1244
+ const originHandler = dynamicOriginCallbackHandler(origin);
1227
1245
  return (req, res, next) => {
1246
+ // Handle OPTIONS preflight requests early for Lambda streaming compatibility.
1247
+ // The standard cors package would eventually call res.end(), but with Lambda
1248
+ // streaming, we need to ensure the response is terminated immediately without
1249
+ // going through any async middleware chains that might keep the stream open.
1250
+ if (req.method === HTTP_METHOD_OPTIONS) {
1251
+ const requestOrigin = req.headers.origin;
1252
+ originHandler(requestOrigin, (error, isAllowed) => {
1253
+ if (error || !isAllowed) {
1254
+ // Origin not allowed - send CORS error
1255
+ const corsError = error;
1256
+ if (corsError?.status && corsError?.body) {
1257
+ res.status(corsError.status);
1258
+ res.setHeader("Content-Type", "application/json");
1259
+ res.json(corsError.body());
1260
+ }
1261
+ else {
1262
+ // Fallback for non-CorsError errors
1263
+ res.status(HTTP_CODE_NO_CONTENT);
1264
+ if (typeof res.flushHeaders === "function") {
1265
+ res.flushHeaders();
1266
+ }
1267
+ res.end();
1268
+ }
1269
+ return;
1270
+ }
1271
+ // Origin is allowed - send preflight response
1272
+ // Set CORS headers
1273
+ if (requestOrigin) {
1274
+ res.setHeader("Access-Control-Allow-Origin", requestOrigin);
1275
+ }
1276
+ res.setHeader("Vary", "Origin");
1277
+ // Allow all methods by default (or use overrides if specified)
1278
+ const methods = overrides.methods || "GET,HEAD,PUT,PATCH,POST,DELETE";
1279
+ res.setHeader("Access-Control-Allow-Methods", methods);
1280
+ // Reflect requested headers (standard cors behavior)
1281
+ const requestedHeaders = req.headers["access-control-request-headers"];
1282
+ if (requestedHeaders) {
1283
+ res.setHeader("Access-Control-Allow-Headers", requestedHeaders);
1284
+ }
1285
+ // Handle credentials if configured
1286
+ if (overrides.credentials === true) {
1287
+ res.setHeader("Access-Control-Allow-Credentials", "true");
1288
+ }
1289
+ // Handle max age if configured
1290
+ if (overrides.maxAge !== undefined) {
1291
+ res.setHeader("Access-Control-Max-Age", String(overrides.maxAge));
1292
+ }
1293
+ // Send 204 No Content response and terminate immediately
1294
+ // This is critical for Lambda streaming - we must end the response
1295
+ // synchronously to prevent the stream from hanging.
1296
+ // CRITICAL: Flush headers first to initialize the Lambda stream wrapper.
1297
+ // Without this, _wrappedStream may be null when _final() is called,
1298
+ // causing the Lambda response stream to never close.
1299
+ res.statusCode = HTTP_CODE_NO_CONTENT;
1300
+ res.setHeader("Content-Length", "0");
1301
+ if (typeof res.flushHeaders === "function") {
1302
+ res.flushHeaders();
1303
+ }
1304
+ res.end();
1305
+ });
1306
+ return;
1307
+ }
1308
+ // For non-OPTIONS requests, use the standard cors middleware
1228
1309
  cors(req, res, (error) => {
1229
1310
  if (error) {
1230
1311
  const corsError = error;