@jaypie/express 1.2.31 → 1.2.33

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.
@@ -0,0 +1,15 @@
1
+ export declare const SET_COOKIE = "set-cookie";
2
+ /**
3
+ * Normalize a header value for storage, preserving arrays.
4
+ * Node's ServerResponse keeps multi-value headers as arrays; stringifying
5
+ * here would fold them into a single comma-separated value.
6
+ */
7
+ export declare function normalizeHeaderValue(value: number | string | string[]): string | string[];
8
+ /**
9
+ * Split stored headers into a single-value header record and a cookies
10
+ * array, matching the Lambda Function URL (v2) response shape.
11
+ */
12
+ export declare function splitCookieHeaders(source: Map<string, string | string[]>): {
13
+ cookies: string[];
14
+ headers: Record<string, string>;
15
+ };
@@ -82,6 +82,7 @@ export interface ResponseStream {
82
82
  write(chunk: string | Uint8Array): void;
83
83
  }
84
84
  export interface HttpResponseStreamMetadata {
85
+ cookies?: string[];
85
86
  headers: Record<string, string>;
86
87
  statusCode: number;
87
88
  }
package/dist/esm/index.js CHANGED
@@ -276,6 +276,50 @@ function createLambdaRequest(event, context) {
276
276
  });
277
277
  }
278
278
 
279
+ //
280
+ //
281
+ // Constants
282
+ //
283
+ // Set-Cookie is the one header that must never be comma-folded: cookie
284
+ // expiry dates contain commas, so a folded value is unparseable. Lambda
285
+ // carries these out-of-band in the `cookies` field of the v2 response
286
+ // payload and of the response-streaming metadata prelude.
287
+ const SET_COOKIE = "set-cookie";
288
+ //
289
+ //
290
+ // Functions
291
+ //
292
+ /**
293
+ * Normalize a header value for storage, preserving arrays.
294
+ * Node's ServerResponse keeps multi-value headers as arrays; stringifying
295
+ * here would fold them into a single comma-separated value.
296
+ */
297
+ function normalizeHeaderValue(value) {
298
+ return Array.isArray(value) ? value.map(String) : String(value);
299
+ }
300
+ /**
301
+ * Split stored headers into a single-value header record and a cookies
302
+ * array, matching the Lambda Function URL (v2) response shape.
303
+ */
304
+ function splitCookieHeaders(source) {
305
+ const cookies = [];
306
+ const headers = {};
307
+ for (const [key, value] of source) {
308
+ if (key === SET_COOKIE) {
309
+ if (Array.isArray(value)) {
310
+ cookies.push(...value);
311
+ }
312
+ else {
313
+ cookies.push(value);
314
+ }
315
+ }
316
+ else {
317
+ headers[key] = Array.isArray(value) ? value.join(", ") : String(value);
318
+ }
319
+ }
320
+ return { cookies, headers };
321
+ }
322
+
279
323
  //
280
324
  //
281
325
  // Constants
@@ -413,13 +457,14 @@ class LambdaResponseBuffered extends Writable {
413
457
  return this;
414
458
  }
415
459
  const lowerName = name.toLowerCase();
416
- this._headers.set(lowerName, String(value));
460
+ const normalized = normalizeHeaderValue(value);
461
+ this._headers.set(lowerName, normalized);
417
462
  // Sync with kOutHeaders for dd-trace compatibility
418
463
  // Node stores as { 'header-name': ['Header-Name', value] }
419
464
  if (kOutHeaders$1) {
420
465
  const outHeaders = this[kOutHeaders$1];
421
466
  if (outHeaders) {
422
- outHeaders[lowerName] = [name, String(value)];
467
+ outHeaders[lowerName] = [name, normalized];
423
468
  }
424
469
  }
425
470
  return this;
@@ -507,7 +552,7 @@ class LambdaResponseBuffered extends Writable {
507
552
  // Use direct _headers access to bypass dd-trace interception
508
553
  for (const [key, value] of Object.entries(headersToSet)) {
509
554
  if (value !== undefined) {
510
- this._headers.set(key.toLowerCase(), String(value));
555
+ this._headers.set(key.toLowerCase(), normalizeHeaderValue(value));
511
556
  }
512
557
  }
513
558
  }
@@ -535,7 +580,7 @@ class LambdaResponseBuffered extends Writable {
535
580
  */
536
581
  set(name, value) {
537
582
  if (!this._headersSent) {
538
- this._headers.set(name.toLowerCase(), String(value));
583
+ this._headers.set(name.toLowerCase(), normalizeHeaderValue(value));
539
584
  }
540
585
  return this;
541
586
  }
@@ -605,23 +650,8 @@ class LambdaResponseBuffered extends Writable {
605
650
  const contentType = this._headers.get("content-type") || "";
606
651
  // Determine if response should be base64 encoded
607
652
  const isBase64Encoded = this.isBinaryContentType(contentType);
608
- // Build headers object
609
- const headers = {};
610
- const cookies = [];
611
- for (const [key, value] of this._headers) {
612
- if (key === "set-cookie") {
613
- // Collect Set-Cookie headers for v2 response format
614
- if (Array.isArray(value)) {
615
- cookies.push(...value);
616
- }
617
- else {
618
- cookies.push(value);
619
- }
620
- }
621
- else {
622
- headers[key] = Array.isArray(value) ? value.join(", ") : String(value);
623
- }
624
- }
653
+ // Build headers object, carrying Set-Cookie out-of-band (v2 format)
654
+ const { cookies, headers } = splitCookieHeaders(this._headers);
625
655
  const result = {
626
656
  body: isBase64Encoded ? body.toString("base64") : body.toString("utf8"),
627
657
  headers,
@@ -754,13 +784,14 @@ class LambdaResponseStreaming extends Writable {
754
784
  return this;
755
785
  }
756
786
  const lowerName = name.toLowerCase();
757
- this._headers.set(lowerName, String(value));
787
+ const normalized = normalizeHeaderValue(value);
788
+ this._headers.set(lowerName, normalized);
758
789
  // Sync with kOutHeaders for dd-trace compatibility
759
790
  // Node stores as { 'header-name': ['Header-Name', value] }
760
791
  if (kOutHeaders) {
761
792
  const outHeaders = this[kOutHeaders];
762
793
  if (outHeaders) {
763
- outHeaders[lowerName] = [name, String(value)];
794
+ outHeaders[lowerName] = [name, normalized];
764
795
  }
765
796
  }
766
797
  return this;
@@ -855,7 +886,7 @@ class LambdaResponseStreaming extends Writable {
855
886
  // Use direct _headers access to bypass dd-trace interception
856
887
  for (const [key, value] of Object.entries(headersToSet)) {
857
888
  if (value !== undefined) {
858
- this._headers.set(key.toLowerCase(), String(value));
889
+ this._headers.set(key.toLowerCase(), normalizeHeaderValue(value));
859
890
  }
860
891
  }
861
892
  }
@@ -869,10 +900,8 @@ class LambdaResponseStreaming extends Writable {
869
900
  if (this._headersSent) {
870
901
  return;
871
902
  }
872
- const headers = {};
873
- for (const [key, value] of this._headers) {
874
- headers[key] = Array.isArray(value) ? value.join(", ") : String(value);
875
- }
903
+ // Set-Cookie travels in metadata.cookies, never folded into headers
904
+ const { cookies, headers } = splitCookieHeaders(this._headers);
876
905
  // Lambda streaming requires body content for metadata to be transmitted.
877
906
  // Convert 204 No Content to 200 OK with empty JSON body as workaround.
878
907
  // See: https://github.com/finlaysonstudio/jaypie/issues/178
@@ -887,6 +916,10 @@ class LambdaResponseStreaming extends Writable {
887
916
  headers,
888
917
  statusCode,
889
918
  };
919
+ // Only include cookies if present (v2 format)
920
+ if (cookies.length > 0) {
921
+ metadata.cookies = cookies;
922
+ }
890
923
  // Create wrapped stream with metadata
891
924
  this._wrappedStream = awslambda.HttpResponseStream.from(this._responseStream, metadata);
892
925
  this._headersSent = true;
@@ -915,7 +948,7 @@ class LambdaResponseStreaming extends Writable {
915
948
  */
916
949
  set(name, value) {
917
950
  if (!this._headersSent) {
918
- this._headers.set(name.toLowerCase(), String(value));
951
+ this._headers.set(name.toLowerCase(), normalizeHeaderValue(value));
919
952
  }
920
953
  return this;
921
954
  }
@@ -1755,7 +1788,6 @@ function safeSend(res, statusCode, body) {
1755
1788
  }
1756
1789
  }
1757
1790
  function expressHandler(handlerOrOptions, optionsOrHandler) {
1758
- /* eslint-enable no-redeclare */
1759
1791
  let handler;
1760
1792
  let options;
1761
1793
  // If handler is an object and options is a function, swap them
@@ -2172,7 +2204,6 @@ function getErrorBody(error) {
2172
2204
  : new UnhandledError().body();
2173
2205
  }
2174
2206
  function expressStreamHandler(handlerOrOptions, optionsOrHandler) {
2175
- /* eslint-enable no-redeclare */
2176
2207
  let handler;
2177
2208
  let options;
2178
2209
  // If handler is an object and options is a function, swap them