add more common headers, use proxy for headers
authorJustin Wind <justin.wind+git@gmail.com>
Fri, 15 Aug 2025 16:27:42 +0000 (09:27 -0700)
committerJustin Wind <justin.wind+git@gmail.com>
Fri, 15 Aug 2025 16:30:15 +0000 (09:30 -0700)
lib/enum.js
test/lib/enum.js

index 02337f7dcd6127d03679a5c8be9d1395549aa67a..5344b8c043a68d3f0b81c9624ff5b53cf554ba36 100644 (file)
@@ -194,6 +194,9 @@ function errorResponseGenerator () {
 
 const ErrorResponse = errorResponseGenerator();
 
+/**
+ * Always provide an error response, 500 if unknown.
+ */
 const ErrorResponseProxy = new Proxy(ErrorResponse, {
   get: (target, property) => {
     if (property in target) {
@@ -210,40 +213,128 @@ const ErrorResponseProxy = new Proxy(ErrorResponse, {
 const Header = {
   Accept: 'Accept',
   AcceptEncoding: 'Accept-Encoding',
+  AcceptLanguage: 'Accept-Language',
+  AcceptPatch: 'Accept-Patch',
+  AcceptPost: 'Accept-Post',
+  AcceptRanges: 'Accept-Ranges',
+  AccessControlAllowCredentials: 'Access-Control-Allow-Credentials',
+  AccessControlAllowHeaders: 'Access-Control-Allow-Headers',
+  AccessControlAllowMethods: 'Access-Control-Allow-Methods',
+  AccessControlAllowOrigin: 'Access-Control-Allow-Origin',
+  AccessControlExposeHeaders: 'Access-Control-Expose-Headers',
+  AccessControlMaxAge: 'Access-Control-Max-Age',
+  AccessControlRequestHeaders: 'Access-Control-Request-Headers',
+  AccessControlRequestMethod: 'Access-Control-Request-Method',
+  Age: 'Age',
   Allow: 'Allow',
+  AltSvc: 'Alt-Svc',
+  AltUsed: 'Alt-Used',
+  Authorization: 'Authorization',
   CacheControl: 'Cache-Control',
+  ClearSiteData: 'Clear-Site-Data',
+  Connection: 'Connection',
   ContentDigest: 'Content-Digest',
+  ContentDisposition: 'Content-Disposition',
   ContentEncoding: 'Content-Encoding',
+  ContentLanguage: 'Content-Language',
   ContentLength: 'Content-Length',
+  ContentLocation: 'Content-Location',
+  ContentRange: 'Content-Range',
+  ContentSecurityPolicy: 'Content-Security-Policy',
+  ContentSecurityPolicyReportOnly: 'Content-Security-Policy-Report-Only',
   ContentType: 'Content-Type',
   Cookie: 'Cookie',
+  CrossOriginEmbedderPolicy: 'Cross-Origin-Embedder-Policy',
+  CrossOriginOpenerPolicy: 'Cross-Origin-Opener-Policy',
+  CrossOriginResourcePolicy: 'Cross-Origin-Resource-Policy',
+  Date: 'Date',
   Digest: 'Digest',
   ETag: 'ETag',
+  Expect: 'Expect',
+  Expires: 'Expires',
+  Forwarded: 'Forwarded',
+  From: 'From',
+  Host: 'Host',
+  IfMatch: 'If-Match',
   IfModifiedSince: 'If-Modified-Since',
   IfNoneMatch: 'If-None-Match',
+  IfUnmodifiedSince: 'If-Unmodified-Since',
+  IntegrityPolicy: 'Integrity-Policy',
+  IntegrityPolicyReportOnly: 'Integrity-Policy-Report-Only',
+  KeepAlive: 'Keep-Alive',
   LastModified: 'Last-Modified',
+  Link: 'Link',
   Location: 'Location',
+  MaxForwards: 'Max-Forwards',
+  Origin: 'Origin',
+  OriginAgentCluster: 'Origin-Agent-Cluster',
+  Prefer: 'Prefer',
+  PreferenceApplied: 'Preference-Applied',
+  Priority: 'Priority',
+  ProxyAuthenticate: 'Proxy-Authenticate',
+  ProxyAuthorization: 'Proxy-Authorization',
+  Range: 'Range',
+  Referer: 'Referer',
+  ReferrerPolicy: 'Referrer-Policy',
+  Refresh: 'Refresh',
+  ReportingEndpoints: 'Reporting-Endpoints',
   ReprDigest: 'Repr-Digest',
+  RetryAfter: 'Retry-After',
   RequestId: 'Request-ID',
+  SecFetchDest: 'Sec-Fetch-Dest',
+  SecFetchMode: 'Sec-Fetch-Mode',
+  SecFetchSite: 'Sec-Fetch-Site',
+  SecFetchUser: 'Sec-Fetch-User',
+  SecPurpose: 'Sec-Purpose',
+  Server: 'Server',
+  ServerTiming: 'Server-Timing',
+  ServiceWorker: 'Service-Worker',
+  ServiceWorkerAllowed: 'Service-Worker-Allowed',
   SetCookie: 'Set-Cookie',
+  SetLogin: 'Set-Login',
+  SourceMap: 'SourceMap',
+  TE: 'TE',
+  TimingAllowOrigin: 'Timing-Allow-Origin',
+  Trailer: 'Trailer',
+  TransferEncoding: 'Transfer-Encoding',
+  Upgrade: 'Upgrade',
+  UpgradeInsecureRequests: 'Upgrade-Insecure-Requests',
+  UserAgent: 'User-Agent',
   Vary: 'Vary',
+  Via: 'Via',
   WantContentDigest: 'Want-Content-Digest',
   WantDigest: 'Want-Digest',
   WantReprDigest: 'Want-Repr-Digest',
+  WWWAuthenticate: 'WWW-Authenticate',
+  XContentTypeOptions: 'X-Content-Type-Options',
   XCorrelationId: 'X-Correlation-ID',
+  XFrameOptions: 'X-Frame-Options',
   XForwardedFor: 'X-Forwarded-For',
   XForwardedProto: 'X-Forwarded-Proto',
   XRealIP: 'X-Real-IP',
   XRequestId: 'X-Request-ID',
 };
 
+/**
+ * Error if referencing an undefined header.
+ */
+const HeaderProxy = new Proxy(Header, {
+  get: (target, property) => {
+    if (property in target) {
+      return target[property]; // eslint-disable-line security/detect-object-injection
+    } else {
+      throw new RangeError(`undefined header '${property}'`);
+    }
+  },
+});
+
 module.exports = {
   ContentType,
   DigestAlgorithm,
   EncodingType,
   EncodingTypeSuffix,
   ErrorResponse: ErrorResponseProxy,
-  Header,
+  Header: HeaderProxy,
   HTTPStatusCode,
   HTTPStatusMessage,
 };
index ee702c678a22b61e2e6c16244ee64fd66f5881cb..58a4ef7996c93fd46de36c53679aca9e82e0fffb 100644 (file)
@@ -2,7 +2,7 @@
 
 const assert = require('node:assert');
 const Enum = require('../../lib/enum');
-const { mergeEnum } = require('../../lib/common');
+const { mergeEnum, mergeDeep } = require('../../lib/common');
 
 describe('Enum', function () {
 
@@ -40,4 +40,61 @@ describe('Enum', function () {
     });
   }); // ErrorResponse
 
+  describe('Header', function () {
+    it('covers broken', function () {
+      assert.throws(() => Enum.Header.XNotAHeader, RangeError);
+    });
+    it('covers success', function () {
+      const result = Enum.Header.Host;
+      const expected = 'Host';
+      assert.deepStrictEqual(result, expected);
+    });
+    it('can be merged', function () {
+      const XSqueep = 'X-Squeep';
+      const localEnum = mergeEnum(Enum.Header, { XSqueep });
+      const newResult = localEnum.XSqueep;
+      assert.strictEqual(newResult, 'X-Squeep');
+      const oldResult = localEnum.Host;
+      assert.strictEqual(oldResult, 'Host');
+      assert.throws(() => localEnum.XNotAHeader, RangeError);
+    });
+  }); // Header
+
+  describe('Sanity', function () {
+    it('sanity for unexpected usage', function () {
+      const localEnum = mergeDeep(Enum, {
+        ContentType: {
+          ApplicationSqueep: 'application/squeep',
+        },
+        Header: {
+          XSqueep: 'X-Squeep',
+        },
+      });
+      assert.strictEqual(localEnum.EncodingType.Identity, 'identity');
+      assert.strictEqual(localEnum.ContentType.TextPlain, 'text/plain');
+      assert.strictEqual(localEnum.ContentType.ApplicationSqueep, 'application/squeep');
+      assert.strictEqual(localEnum.Header.Host, 'Host');
+      assert.strictEqual(localEnum.Header.XSqueep, 'X-Squeep');
+
+      assert.strictEqual(localEnum.Header.XNotAHeader, undefined);
+      // assert.throws(() => localEnum.Header.XNotAHeader, RangeError); // This returns undefined with deep merge.
+    });
+    it('sanity', function () {
+      const localEnum = mergeEnum(Enum, {
+        ContentType: {
+          ApplicationSqueep: 'application/squeep',
+        },
+        Header: {
+          XSqueep: 'X-Squeep',
+        },
+      });
+      assert.strictEqual(localEnum.EncodingType.Identity, 'identity');
+      assert.strictEqual(localEnum.ContentType.TextPlain, 'text/plain');
+      assert.strictEqual(localEnum.ContentType.ApplicationSqueep, 'application/squeep');
+      assert.strictEqual(localEnum.Header.Host, 'Host');
+      assert.strictEqual(localEnum.Header.XSqueep, 'X-Squeep');
+      assert.throws(() => localEnum.Header.XNotAHeader, RangeError);
+    });
+  }); // sanity
+
 }); // Enum